@hienlh/ppm 0.23.6 → 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 +84 -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 +29 -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-Dn_wB-o5.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-BA3y-qSH.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-BioxkrQF.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-BSoJ0E6p.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-DHh5QsH9.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-lctWoSmG.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-rbkXsZVR.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-uPO0WgCq.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-CT7mDb0L.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-DANPfMRN.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-BJPkLiEw.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-R4PcAntf.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-DHRoyWcG.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-BKej4mUf.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-BHtYqkYZ.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-DS_XrpTG.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-D8AES1BA.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-DTBG4beI.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-CqtmbeRj.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-Djt6cpNL.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-DNJF8TcF.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-BKoPovy2.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-BjIMi9Wk.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-BNcA6Irq.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-CIRGNCvi.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-CwXpLi3X.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-4swiiDz7.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-BGcdtMMT.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-D0-krtA2.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-C88KphGx.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-Chk9guIU.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/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/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 +16 -3
- package/src/services/compaction-savings.ts +127 -0
- package/src/services/db.service.ts +123 -8
- 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/shared/prompt-cache-idle.ts +197 -0
- package/src/shared/turn-usage.ts +85 -1
- package/src/types/chat.ts +27 -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/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 +5 -1
- 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-DUVO32SQ.js +0 -1
- package/dist/web/assets/accounts-settings-section-DUVO32SQ.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-DUVO32SQ.js.gz +0 -0
- package/dist/web/assets/ai-resource-editor-Dn_wB-o5.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-Dn_wB-o5.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-CEbW8jRy.js +0 -1
- package/dist/web/assets/ai-settings-section-CEbW8jRy.js.br +0 -0
- package/dist/web/assets/ai-settings-section-CEbW8jRy.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-D-mwEYFZ.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-tw6TpFb6.js +0 -1
- package/dist/web/assets/appearance-settings-section-tw6TpFb6.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-tw6TpFb6.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-MBuMWp_i.js +0 -1
- package/dist/web/assets/branch-review-tab-MBuMWp_i.js.br +0 -0
- package/dist/web/assets/branch-review-tab-MBuMWp_i.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-DVvnqhw6.js +0 -1
- package/dist/web/assets/chat-tab-DVvnqhw6.js.br +0 -0
- package/dist/web/assets/chat-tab-DVvnqhw6.js.gz +0 -0
- package/dist/web/assets/chat-tab-DaEHva22.js +0 -15
- package/dist/web/assets/chat-tab-DaEHva22.js.br +0 -0
- package/dist/web/assets/chat-tab-DaEHva22.js.gz +0 -0
- package/dist/web/assets/code-editor-Bu9lhTVp.js +0 -7
- package/dist/web/assets/code-editor-Bu9lhTVp.js.br +0 -0
- package/dist/web/assets/code-editor-Bu9lhTVp.js.gz +0 -0
- package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js +0 -1
- package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-CbmE5mRX.js.gz +0 -0
- package/dist/web/assets/conflict-editor-BA3y-qSH.js.br +0 -0
- package/dist/web/assets/conflict-editor-BA3y-qSH.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-BioxkrQF.js.br +0 -0
- package/dist/web/assets/csv-preview-BioxkrQF.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-BSoJ0E6p.js.br +0 -0
- package/dist/web/assets/database-viewer-BSoJ0E6p.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-iU2CviE3.js +0 -33
- package/dist/web/assets/design-tab-iU2CviE3.js.br +0 -0
- package/dist/web/assets/design-tab-iU2CviE3.js.gz +0 -0
- package/dist/web/assets/designs-sidebar-panel-DHh5QsH9.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-DHh5QsH9.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-lctWoSmG.js.br +0 -0
- package/dist/web/assets/diff-viewer-lctWoSmG.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-fPG56hQO.js +0 -1
- package/dist/web/assets/editor-language-service-fPG56hQO.js.br +0 -0
- package/dist/web/assets/editor-language-service-fPG56hQO.js.gz +0 -0
- package/dist/web/assets/explorer-body-BgF7FsxI.js +0 -2
- package/dist/web/assets/explorer-body-BgF7FsxI.js.br +0 -0
- package/dist/web/assets/explorer-body-BgF7FsxI.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-C21w_7lf.js +0 -1
- package/dist/web/assets/explorer-window-content-C21w_7lf.js.br +0 -0
- package/dist/web/assets/explorer-window-content-C21w_7lf.js.gz +0 -0
- package/dist/web/assets/extension-manager-section-rbkXsZVR.js.br +0 -0
- package/dist/web/assets/extension-manager-section-rbkXsZVR.js.gz +0 -0
- package/dist/web/assets/extension-webview-uPO0WgCq.js.br +0 -0
- package/dist/web/assets/extension-webview-uPO0WgCq.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-CT7mDb0L.js.br +0 -0
- package/dist/web/assets/files-settings-section-CT7mDb0L.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-DANPfMRN.js.br +0 -0
- package/dist/web/assets/general-settings-section-DANPfMRN.js.gz +0 -0
- package/dist/web/assets/git-log-panel-BJPkLiEw.js.br +0 -0
- package/dist/web/assets/git-log-panel-BJPkLiEw.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-R4PcAntf.js.br +0 -0
- package/dist/web/assets/glide-data-grid-R4PcAntf.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-BXMdo8w9.js +0 -1
- package/dist/web/assets/group-chat-tab-BXMdo8w9.js.br +0 -0
- package/dist/web/assets/group-chat-tab-BXMdo8w9.js.gz +0 -0
- package/dist/web/assets/index-C8nGqkKN.js +0 -52
- package/dist/web/assets/index-C8nGqkKN.js.br +0 -0
- package/dist/web/assets/index-C8nGqkKN.js.gz +0 -0
- package/dist/web/assets/index-DvClbTQx.css +0 -1
- package/dist/web/assets/index-DvClbTQx.css.br +0 -0
- package/dist/web/assets/index-DvClbTQx.css.gz +0 -0
- package/dist/web/assets/keybindings-store-BGKlQQAv.js +0 -1
- package/dist/web/assets/keyboard-shortcuts-section-BKej4mUf.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-BKej4mUf.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-BHtYqkYZ.js.br +0 -0
- package/dist/web/assets/markdown-renderer-BHtYqkYZ.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-vDNuKjel.js +0 -1
- 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-DtvxS7-r.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-DtvxS7-r.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-DS_XrpTG.js.br +0 -0
- package/dist/web/assets/new-design-dialog-DS_XrpTG.js.gz +0 -0
- package/dist/web/assets/notification-store-Dnnj20Ky.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-D8AES1BA.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-D8AES1BA.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-DTBG4beI.js.br +0 -0
- package/dist/web/assets/problems-panel-DTBG4beI.js.gz +0 -0
- package/dist/web/assets/process-table-D_gR2XA6.js +0 -1
- package/dist/web/assets/process-table-D_gR2XA6.js.br +0 -0
- package/dist/web/assets/process-table-D_gR2XA6.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-CqtmbeRj.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-CqtmbeRj.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-Djt6cpNL.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-Djt6cpNL.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BQxhJUkm.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DGnT8RY7.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-DNJF8TcF.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-DNJF8TcF.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-Ds0DKmM0.js.gz +0 -0
- package/dist/web/assets/schedules-settings-section-BKoPovy2.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-BKoPovy2.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-k2qPgRFe.js +0 -2
- package/dist/web/assets/settings-body-k2qPgRFe.js.br +0 -0
- package/dist/web/assets/settings-body-k2qPgRFe.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-Bb23VdBu.js +0 -1
- package/dist/web/assets/settings-window-content-ClarWusz.js +0 -1
- package/dist/web/assets/sql-query-editor-BjIMi9Wk.js.br +0 -0
- package/dist/web/assets/sql-query-editor-BjIMi9Wk.js.gz +0 -0
- package/dist/web/assets/sqlite-viewer-BNcA6Irq.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-BNcA6Irq.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-D0T3ggt2.js +0 -2
- package/dist/web/assets/system-monitor-body-D0T3ggt2.js.br +0 -0
- package/dist/web/assets/system-monitor-body-D0T3ggt2.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-I1Igmoey.js +0 -1
- package/dist/web/assets/system-monitor-window-content-C5SeUv1H.js +0 -1
- package/dist/web/assets/tab-host-window-content-CIRGNCvi.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-CIRGNCvi.js.gz +0 -0
- package/dist/web/assets/team-member-sheet-CwXpLi3X.js.br +0 -0
- package/dist/web/assets/team-member-sheet-CwXpLi3X.js.gz +0 -0
- package/dist/web/assets/team-member-window-content-4swiiDz7.js.br +0 -0
- package/dist/web/assets/team-member-window-content-4swiiDz7.js.gz +0 -0
- package/dist/web/assets/terminal-tab-BGcdtMMT.js.br +0 -0
- package/dist/web/assets/terminal-tab-BGcdtMMT.js.gz +0 -0
- package/dist/web/assets/tool-cards-D0-krtA2.js.br +0 -0
- package/dist/web/assets/tool-cards-D0-krtA2.js.gz +0 -0
- package/dist/web/assets/use-inline-blame-pKaXyGU-.js +0 -16
- package/dist/web/assets/use-inline-blame-pKaXyGU-.js.br +0 -0
- package/dist/web/assets/use-inline-blame-pKaXyGU-.js.gz +0 -0
- package/dist/web/assets/use-lsp-71azTXQz.js +0 -6
- package/dist/web/assets/use-lsp-71azTXQz.js.br +0 -0
- package/dist/web/assets/use-lsp-71azTXQz.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-DxEX-uGo.js +0 -1
- package/dist/web/assets/video-preview-DxEX-uGo.js.br +0 -0
- package/dist/web/assets/video-preview-DxEX-uGo.js.gz +0 -0
- package/dist/web/assets/ws-auth-kXgsX2Js.js +0 -1
- /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,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Voice Input settings: which engine the chat mic uses, and the install button
|
|
3
|
+
* for the one that runs on the host.
|
|
4
|
+
*
|
|
5
|
+
* "Whisper on this host" only becomes selectable once it is actually installed
|
|
6
|
+
* — offering an engine that answers 409 would be a setting that silently does
|
|
7
|
+
* nothing. Until then this pane is the install screen for it.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
11
|
+
import { toast } from "sonner";
|
|
12
|
+
import { Mic, Loader2, Download, Trash2, Check } from "@/lib/icons";
|
|
13
|
+
import { Button } from "@/components/ui/button";
|
|
14
|
+
import { Separator } from "@/components/ui/separator";
|
|
15
|
+
import { api } from "@/lib/api-client";
|
|
16
|
+
import { cn } from "@/lib/utils";
|
|
17
|
+
import { useSettingsStore, type VoiceEngine } from "@/stores/settings-store";
|
|
18
|
+
|
|
19
|
+
interface StatusModel {
|
|
20
|
+
id: string;
|
|
21
|
+
label: string;
|
|
22
|
+
note: string;
|
|
23
|
+
bytes: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface InstallJob {
|
|
27
|
+
modelId: string;
|
|
28
|
+
phase: "binary" | "model" | "vad" | "verify";
|
|
29
|
+
receivedBytes: number;
|
|
30
|
+
totalBytes: number;
|
|
31
|
+
done: boolean;
|
|
32
|
+
error: string | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface SpeechStatus {
|
|
36
|
+
installable: boolean;
|
|
37
|
+
installHint: string | null;
|
|
38
|
+
binary: { path: string; source: "bundled" | "system" } | null;
|
|
39
|
+
version: string;
|
|
40
|
+
model: StatusModel | null;
|
|
41
|
+
ready: boolean;
|
|
42
|
+
models: StatusModel[];
|
|
43
|
+
install: InstallJob | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const PHASE_LABEL: Record<InstallJob["phase"], string> = {
|
|
47
|
+
binary: "Downloading whisper.cpp",
|
|
48
|
+
model: "Downloading the model",
|
|
49
|
+
vad: "Downloading the voice detector",
|
|
50
|
+
verify: "Checking it runs here",
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const mb = (bytes: number) => `${Math.round(bytes / 1e6)} MB`;
|
|
54
|
+
|
|
55
|
+
export function VoiceSettingsSection() {
|
|
56
|
+
const voiceEngine = useSettingsStore((s) => s.voiceEngine);
|
|
57
|
+
const setVoiceEngine = useSettingsStore((s) => s.setVoiceEngine);
|
|
58
|
+
|
|
59
|
+
const [status, setStatus] = useState<SpeechStatus | null>(null);
|
|
60
|
+
const [selectedModel, setSelectedModel] = useState<string | null>(null);
|
|
61
|
+
const [busy, setBusy] = useState(false);
|
|
62
|
+
const pollRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
63
|
+
|
|
64
|
+
const load = useCallback(async () => {
|
|
65
|
+
try {
|
|
66
|
+
const next = await api.get<SpeechStatus>("/api/speech/status");
|
|
67
|
+
setStatus(next);
|
|
68
|
+
setSelectedModel((current) => current ?? next.model?.id ?? next.models.at(-1)?.id ?? null);
|
|
69
|
+
} catch {
|
|
70
|
+
// A status that cannot be read leaves the pane on "not installed", which
|
|
71
|
+
// is what the user can act on anyway.
|
|
72
|
+
}
|
|
73
|
+
}, []);
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
void load();
|
|
77
|
+
}, [load]);
|
|
78
|
+
|
|
79
|
+
// Poll only while an install is running — 600 MB takes a while and the phase
|
|
80
|
+
// and byte count are the only sign that anything is happening.
|
|
81
|
+
const installing = !!status?.install && !status.install.done;
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (!installing) return;
|
|
84
|
+
pollRef.current = setTimeout(() => void load(), 700);
|
|
85
|
+
return () => {
|
|
86
|
+
if (pollRef.current) clearTimeout(pollRef.current);
|
|
87
|
+
};
|
|
88
|
+
}, [installing, status, load]);
|
|
89
|
+
|
|
90
|
+
// An engine that was removed must not stay selected.
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (status && !status.ready && voiceEngine === "whisper") setVoiceEngine("browser");
|
|
93
|
+
}, [status, voiceEngine, setVoiceEngine]);
|
|
94
|
+
|
|
95
|
+
const install = async () => {
|
|
96
|
+
if (!selectedModel) return;
|
|
97
|
+
setBusy(true);
|
|
98
|
+
try {
|
|
99
|
+
await api.post("/api/speech/install", { model: selectedModel });
|
|
100
|
+
await load();
|
|
101
|
+
} catch (e) {
|
|
102
|
+
toast.error("Could not start the install", { description: e instanceof Error ? e.message : String(e) });
|
|
103
|
+
} finally {
|
|
104
|
+
setBusy(false);
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const uninstall = async () => {
|
|
109
|
+
setBusy(true);
|
|
110
|
+
try {
|
|
111
|
+
await api.post("/api/speech/uninstall");
|
|
112
|
+
setVoiceEngine("browser");
|
|
113
|
+
await load();
|
|
114
|
+
} catch (e) {
|
|
115
|
+
toast.error("Could not remove Whisper", { description: e instanceof Error ? e.message : String(e) });
|
|
116
|
+
} finally {
|
|
117
|
+
setBusy(false);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const job = status?.install ?? null;
|
|
122
|
+
const percent = job && job.totalBytes > 0 ? Math.round((job.receivedBytes / job.totalBytes) * 100) : null;
|
|
123
|
+
const insecure = typeof window !== "undefined" && !window.isSecureContext;
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<div className="space-y-6">
|
|
127
|
+
<section className="space-y-3">
|
|
128
|
+
<div>
|
|
129
|
+
<h3 className="text-sm font-medium">Speech to text</h3>
|
|
130
|
+
<p className="text-xs text-muted-foreground">
|
|
131
|
+
What the mic button in the chat box uses. This device only.
|
|
132
|
+
</p>
|
|
133
|
+
</div>
|
|
134
|
+
<div className="flex flex-col sm:flex-row gap-2">
|
|
135
|
+
<EngineOption
|
|
136
|
+
label="Browser"
|
|
137
|
+
note="Text appears as you speak. Chrome, Edge and Safari only — the audio goes to their servers."
|
|
138
|
+
value="browser"
|
|
139
|
+
current={voiceEngine}
|
|
140
|
+
onChange={setVoiceEngine}
|
|
141
|
+
/>
|
|
142
|
+
{status?.ready && (
|
|
143
|
+
<EngineOption
|
|
144
|
+
label="Whisper on this host"
|
|
145
|
+
note="Works in any browser and the audio never leaves your machine. Text arrives when you stop talking."
|
|
146
|
+
value="whisper"
|
|
147
|
+
current={voiceEngine}
|
|
148
|
+
onChange={setVoiceEngine}
|
|
149
|
+
/>
|
|
150
|
+
)}
|
|
151
|
+
</div>
|
|
152
|
+
{insecure && (
|
|
153
|
+
<p className="text-xs text-warning">
|
|
154
|
+
This page is on plain HTTP, where browsers block the microphone entirely. Open PPM over HTTPS or on
|
|
155
|
+
localhost to use either engine.
|
|
156
|
+
</p>
|
|
157
|
+
)}
|
|
158
|
+
</section>
|
|
159
|
+
|
|
160
|
+
<Separator />
|
|
161
|
+
|
|
162
|
+
<section className="space-y-3">
|
|
163
|
+
<div>
|
|
164
|
+
<h3 className="text-sm font-medium">Whisper on this host</h3>
|
|
165
|
+
<p className="text-xs text-muted-foreground">
|
|
166
|
+
{status?.ready
|
|
167
|
+
? `Installed — whisper.cpp ${status.version}, model ${status.model?.label}.`
|
|
168
|
+
: "Runs speech to text on the machine PPM is installed on. Nothing is sent to a cloud service."}
|
|
169
|
+
</p>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
{status && !status.installable && (
|
|
173
|
+
<p className="text-xs text-muted-foreground">
|
|
174
|
+
There is no prebuilt whisper.cpp for this platform. Install it yourself and PPM will find it:{" "}
|
|
175
|
+
<code className="rounded bg-muted px-1 py-0.5">{status.installHint ?? "whisper-cli on PATH"}</code>
|
|
176
|
+
</p>
|
|
177
|
+
)}
|
|
178
|
+
|
|
179
|
+
{job && !job.done && (
|
|
180
|
+
<div className="space-y-2">
|
|
181
|
+
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
182
|
+
<Loader2 className="size-3.5 animate-spin" />
|
|
183
|
+
<span>
|
|
184
|
+
{PHASE_LABEL[job.phase]}
|
|
185
|
+
{percent === null ? "…" : ` — ${percent}%`}
|
|
186
|
+
</span>
|
|
187
|
+
</div>
|
|
188
|
+
<div className="h-1.5 w-full overflow-hidden rounded-full bg-muted">
|
|
189
|
+
<div className="h-full bg-primary transition-[width]" style={{ width: `${percent ?? 0}%` }} />
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
)}
|
|
193
|
+
|
|
194
|
+
{job?.error && <p className="text-xs text-error">{job.error}</p>}
|
|
195
|
+
|
|
196
|
+
{status?.installable && (
|
|
197
|
+
<>
|
|
198
|
+
<div className="space-y-2">
|
|
199
|
+
{status.models.map((model) => {
|
|
200
|
+
const active = selectedModel === model.id;
|
|
201
|
+
const installed = status.model?.id === model.id;
|
|
202
|
+
return (
|
|
203
|
+
<button
|
|
204
|
+
key={model.id}
|
|
205
|
+
type="button"
|
|
206
|
+
onClick={() => setSelectedModel(model.id)}
|
|
207
|
+
className={cn(
|
|
208
|
+
"w-full rounded-lg border px-4 py-3 text-left transition-colors",
|
|
209
|
+
active ? "border-primary ring-2 ring-primary" : "border-border hover:bg-muted/50",
|
|
210
|
+
)}
|
|
211
|
+
>
|
|
212
|
+
<div className="flex items-center justify-between gap-3">
|
|
213
|
+
<span className="text-sm font-medium">{model.label}</span>
|
|
214
|
+
<span className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
215
|
+
{installed && <Check className="size-3.5 text-success" />}
|
|
216
|
+
{mb(model.bytes)}
|
|
217
|
+
</span>
|
|
218
|
+
</div>
|
|
219
|
+
<p className="text-xs text-muted-foreground">{model.note}</p>
|
|
220
|
+
</button>
|
|
221
|
+
);
|
|
222
|
+
})}
|
|
223
|
+
</div>
|
|
224
|
+
|
|
225
|
+
<div className="flex flex-col sm:flex-row gap-2">
|
|
226
|
+
<Button
|
|
227
|
+
onClick={install}
|
|
228
|
+
disabled={busy || installing || !selectedModel || status.model?.id === selectedModel}
|
|
229
|
+
className="flex-1 gap-2 cursor-pointer"
|
|
230
|
+
>
|
|
231
|
+
<Download className="size-4" />
|
|
232
|
+
{status.model
|
|
233
|
+
? status.model.id === selectedModel
|
|
234
|
+
? "Installed"
|
|
235
|
+
: `Switch to ${status.models.find((m) => m.id === selectedModel)?.label}`
|
|
236
|
+
: "Install"}
|
|
237
|
+
</Button>
|
|
238
|
+
{(status.model || status.binary?.source === "bundled") && (
|
|
239
|
+
<Button
|
|
240
|
+
variant="outline"
|
|
241
|
+
onClick={uninstall}
|
|
242
|
+
disabled={busy || installing}
|
|
243
|
+
className="gap-2 cursor-pointer text-error"
|
|
244
|
+
>
|
|
245
|
+
<Trash2 className="size-4" />
|
|
246
|
+
Remove
|
|
247
|
+
</Button>
|
|
248
|
+
)}
|
|
249
|
+
</div>
|
|
250
|
+
</>
|
|
251
|
+
)}
|
|
252
|
+
</section>
|
|
253
|
+
</div>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function EngineOption({
|
|
258
|
+
label, note, value, current, onChange,
|
|
259
|
+
}: {
|
|
260
|
+
label: string;
|
|
261
|
+
note: string;
|
|
262
|
+
value: VoiceEngine;
|
|
263
|
+
current: VoiceEngine;
|
|
264
|
+
onChange: (engine: VoiceEngine) => void;
|
|
265
|
+
}) {
|
|
266
|
+
const active = current === value;
|
|
267
|
+
return (
|
|
268
|
+
<button
|
|
269
|
+
type="button"
|
|
270
|
+
onClick={() => onChange(value)}
|
|
271
|
+
className={cn(
|
|
272
|
+
"flex-1 rounded-lg border px-4 py-3 text-left transition-colors",
|
|
273
|
+
active ? "border-primary ring-2 ring-primary" : "border-border hover:bg-muted/50",
|
|
274
|
+
)}
|
|
275
|
+
>
|
|
276
|
+
<span className="flex items-center gap-2 text-sm font-medium">
|
|
277
|
+
<Mic className="size-4" />
|
|
278
|
+
{label}
|
|
279
|
+
</span>
|
|
280
|
+
<p className="mt-1 text-xs text-muted-foreground">{note}</p>
|
|
281
|
+
</button>
|
|
282
|
+
);
|
|
283
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One application in the Apps list.
|
|
3
|
+
*
|
|
4
|
+
* The icon is fetched from `/api/system/app-icon/<id>`, which resolves the app's
|
|
5
|
+
* own desktop entry server-side — the URL names an app, never a file. An `<img>`
|
|
6
|
+
* cannot send an Authorization header, so the token rides the query string the
|
|
7
|
+
* same way the metrics stream's does; if it still fails the row falls back to a
|
|
8
|
+
* letter tile rather than showing a broken image.
|
|
9
|
+
*/
|
|
10
|
+
import { useState } from "react";
|
|
11
|
+
import {
|
|
12
|
+
ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger,
|
|
13
|
+
} from "@/components/ui/adaptive-context-menu";
|
|
14
|
+
import { getAuthToken } from "@/lib/api-client";
|
|
15
|
+
import { formatRam } from "@/lib/format-bytes";
|
|
16
|
+
import { formatSwapCell } from "../process-row-format";
|
|
17
|
+
import { cn } from "@/lib/utils";
|
|
18
|
+
import type { AppRow as AppRowData } from "./app-rows";
|
|
19
|
+
|
|
20
|
+
export function appIconUrl(appId: string): string {
|
|
21
|
+
const token = getAuthToken();
|
|
22
|
+
const query = token ? `?token=${encodeURIComponent(token)}` : "";
|
|
23
|
+
return `/api/system/app-icon/${encodeURIComponent(appId)}${query}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function cpuColor(pct: number): string {
|
|
27
|
+
if (pct > 80) return "text-error";
|
|
28
|
+
if (pct > 50) return "text-warning";
|
|
29
|
+
return "text-text-secondary";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AppRowProps {
|
|
33
|
+
app: AppRowData;
|
|
34
|
+
/** True when any of its processes is refused by the server's kill guard. */
|
|
35
|
+
endProtected: boolean;
|
|
36
|
+
onEnd: (app: AppRowData) => void;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function AppRow({ app, endProtected, onEnd }: AppRowProps) {
|
|
40
|
+
const [iconFailed, setIconFailed] = useState(false);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<ContextMenu>
|
|
44
|
+
<ContextMenuTrigger asChild>
|
|
45
|
+
<div className="select-none">
|
|
46
|
+
<div
|
|
47
|
+
className="w-full min-h-11 px-3 py-2 flex items-center gap-3 hover:bg-surface-hover transition-colors"
|
|
48
|
+
data-testid="sysmon-app-row"
|
|
49
|
+
data-app-id={app.id}
|
|
50
|
+
data-cpu={app.cpu}
|
|
51
|
+
>
|
|
52
|
+
{app.icon && !iconFailed ? (
|
|
53
|
+
<img
|
|
54
|
+
src={appIconUrl(app.id)}
|
|
55
|
+
alt=""
|
|
56
|
+
width={24}
|
|
57
|
+
height={24}
|
|
58
|
+
className="size-6 shrink-0 object-contain"
|
|
59
|
+
onError={() => setIconFailed(true)}
|
|
60
|
+
/>
|
|
61
|
+
) : (
|
|
62
|
+
<span
|
|
63
|
+
aria-hidden
|
|
64
|
+
className="size-6 shrink-0 rounded bg-surface-hover grid place-items-center text-[11px] text-text-subtle"
|
|
65
|
+
>
|
|
66
|
+
{app.name.slice(0, 1).toUpperCase()}
|
|
67
|
+
</span>
|
|
68
|
+
)}
|
|
69
|
+
<span className="flex-1 min-w-0">
|
|
70
|
+
<span className="block text-sm truncate">{app.name}</span>
|
|
71
|
+
<span className="block text-[11px] text-text-subtle">
|
|
72
|
+
{app.processCount} process{app.processCount === 1 ? "" : "es"}
|
|
73
|
+
</span>
|
|
74
|
+
</span>
|
|
75
|
+
<span className={cn("w-14 text-right text-xs tabular-nums", cpuColor(app.cpu))}>
|
|
76
|
+
{app.cpu.toFixed(1)}%
|
|
77
|
+
</span>
|
|
78
|
+
<span className="w-16 text-right text-xs tabular-nums text-text-secondary">
|
|
79
|
+
{formatRam(app.ramMB)}
|
|
80
|
+
</span>
|
|
81
|
+
{/* Swap only once the host has measured it for somebody: on a machine
|
|
82
|
+
with no per-process source the column would be a wall of dashes. */}
|
|
83
|
+
{app.swapMB !== undefined && (
|
|
84
|
+
<span className="hidden @xs:block w-16 text-right text-xs tabular-nums text-text-subtle">
|
|
85
|
+
{formatSwapCell(app.swapMB)}
|
|
86
|
+
</span>
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</ContextMenuTrigger>
|
|
91
|
+
<ContextMenuContent>
|
|
92
|
+
<ContextMenuItem
|
|
93
|
+
variant="destructive"
|
|
94
|
+
disabled={endProtected}
|
|
95
|
+
onSelect={() => { if (!endProtected) onEnd(app); }}
|
|
96
|
+
>
|
|
97
|
+
End application
|
|
98
|
+
</ContextMenuItem>
|
|
99
|
+
{endProtected && (
|
|
100
|
+
<>
|
|
101
|
+
<ContextMenuSeparator />
|
|
102
|
+
<p className="px-2 py-1.5 text-[11px] text-text-subtle max-w-64">
|
|
103
|
+
One of this app's processes is protected, so PPM will not end it.
|
|
104
|
+
</p>
|
|
105
|
+
</>
|
|
106
|
+
)}
|
|
107
|
+
</ContextMenuContent>
|
|
108
|
+
</ContextMenu>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Apps page's rows - Mission Center's Apps section.
|
|
3
|
+
*
|
|
4
|
+
* An app's figures are the sum over its whole process SUBTREE, not over the pids
|
|
5
|
+
* the server listed: the server sends only the primary pids (each the root of a
|
|
6
|
+
* subtree), because sending every member would make a browser re-walk the same
|
|
7
|
+
* tree anyway. Chromium-shaped apps are dozens of processes under one root, and
|
|
8
|
+
* a row showing only the root's own CPU reads as permanently idle.
|
|
9
|
+
*
|
|
10
|
+
* Pure and React-free. Relative imports only, so a unit test never reaches a
|
|
11
|
+
* zustand store.
|
|
12
|
+
*/
|
|
13
|
+
import type { AppInfo, ProcessInfo } from "../../../../types/system-metrics";
|
|
14
|
+
|
|
15
|
+
export interface AppRow {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
icon: string | null;
|
|
19
|
+
/** The roots the server named; each stands for a subtree. */
|
|
20
|
+
pids: number[];
|
|
21
|
+
/** Every process in those subtrees, the roots included. Ending an app means
|
|
22
|
+
* ending all of these: the roots alone would leave the helpers running, and
|
|
23
|
+
* several roots means there is no single tree kill that covers them. */
|
|
24
|
+
memberPids: number[];
|
|
25
|
+
processCount: number;
|
|
26
|
+
cpu: number;
|
|
27
|
+
ramMB: number;
|
|
28
|
+
/** Summed over the members that HAVE a value, and undefined when none does -
|
|
29
|
+
* so "nothing measurable here" never renders as a confident zero. */
|
|
30
|
+
swapMB?: number;
|
|
31
|
+
diskReadBps?: number;
|
|
32
|
+
diskWriteBps?: number;
|
|
33
|
+
gpuPct?: number;
|
|
34
|
+
gpuMemMB?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** pid to its direct children, built once per tick rather than per app. */
|
|
38
|
+
export function childIndex(processes: readonly ProcessInfo[]): Map<number, number[]> {
|
|
39
|
+
const children = new Map<number, number[]>();
|
|
40
|
+
for (const p of processes) {
|
|
41
|
+
const list = children.get(p.ppid);
|
|
42
|
+
if (list) list.push(p.pid);
|
|
43
|
+
else children.set(p.ppid, [p.pid]);
|
|
44
|
+
}
|
|
45
|
+
return children;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Every pid in the subtrees rooted at `roots`, deduplicated.
|
|
50
|
+
*
|
|
51
|
+
* The `seen` set is not only for shared descendants: a `/proc` walk can observe a
|
|
52
|
+
* process being reparented and produce a parent chain that loops, and without the
|
|
53
|
+
* guard this recurses until the stack goes.
|
|
54
|
+
*/
|
|
55
|
+
export function subtreePids(roots: readonly number[], children: Map<number, number[]>): number[] {
|
|
56
|
+
const seen = new Set<number>();
|
|
57
|
+
const stack = [...roots];
|
|
58
|
+
while (stack.length > 0) {
|
|
59
|
+
const pid = stack.pop()!;
|
|
60
|
+
if (seen.has(pid)) continue;
|
|
61
|
+
seen.add(pid);
|
|
62
|
+
for (const child of children.get(pid) ?? []) stack.push(child);
|
|
63
|
+
}
|
|
64
|
+
return [...seen];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Undefined only when NO member measured the figure at all. */
|
|
68
|
+
function sumOptional(values: readonly (number | undefined)[]): number | undefined {
|
|
69
|
+
let total = 0;
|
|
70
|
+
let any = false;
|
|
71
|
+
for (const v of values) {
|
|
72
|
+
if (v === undefined) continue;
|
|
73
|
+
any = true;
|
|
74
|
+
total += v;
|
|
75
|
+
}
|
|
76
|
+
return any ? total : undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const round1 = (n: number) => Math.round(n * 10) / 10;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* One row per app that still has a live process. An app whose every process
|
|
83
|
+
* exited between the collector and the frame is dropped rather than shown as a
|
|
84
|
+
* zero row - it is not running any more.
|
|
85
|
+
*/
|
|
86
|
+
export function buildAppRows(apps: readonly AppInfo[], processes: readonly ProcessInfo[]): AppRow[] {
|
|
87
|
+
const byPid = new Map(processes.map((p) => [p.pid, p]));
|
|
88
|
+
const children = childIndex(processes);
|
|
89
|
+
const rows: AppRow[] = [];
|
|
90
|
+
|
|
91
|
+
for (const app of apps) {
|
|
92
|
+
const members = subtreePids(app.pids, children)
|
|
93
|
+
.map((pid) => byPid.get(pid))
|
|
94
|
+
.filter((p): p is ProcessInfo => p !== undefined);
|
|
95
|
+
if (members.length === 0) continue;
|
|
96
|
+
|
|
97
|
+
rows.push({
|
|
98
|
+
id: app.id,
|
|
99
|
+
name: app.name,
|
|
100
|
+
icon: app.icon,
|
|
101
|
+
pids: app.pids,
|
|
102
|
+
memberPids: members.map((p) => p.pid),
|
|
103
|
+
processCount: members.length,
|
|
104
|
+
cpu: round1(members.reduce((sum, p) => sum + p.cpu, 0)),
|
|
105
|
+
ramMB: round1(members.reduce((sum, p) => sum + p.ramMB, 0)),
|
|
106
|
+
swapMB: sumOptional(members.map((p) => p.swapMB)),
|
|
107
|
+
diskReadBps: sumOptional(members.map((p) => p.diskReadBps)),
|
|
108
|
+
diskWriteBps: sumOptional(members.map((p) => p.diskWriteBps)),
|
|
109
|
+
// Engine busy can genuinely exceed 100 summed across processes sharing a
|
|
110
|
+
// GPU, which is the same clamp the server applies to a process group.
|
|
111
|
+
gpuPct: clampPercent(sumOptional(members.map((p) => p.gpuPct))),
|
|
112
|
+
gpuMemMB: sumOptional(members.map((p) => p.gpuMemMB)),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return rows;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function clampPercent(value: number | undefined): number | undefined {
|
|
119
|
+
return value === undefined ? undefined : round1(Math.min(100, Math.max(0, value)));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export type AppSortKey = "name" | "cpu" | "ram";
|
|
123
|
+
|
|
124
|
+
/** Busiest first is what a task manager is for; name is the tiebreaker so the
|
|
125
|
+
* order does not churn between ticks when several apps sit at 0%. */
|
|
126
|
+
export function sortAppRows(rows: readonly AppRow[], key: AppSortKey, dir: "asc" | "desc"): AppRow[] {
|
|
127
|
+
const sign = dir === "asc" ? 1 : -1;
|
|
128
|
+
return [...rows].sort((a, b) => {
|
|
129
|
+
if (key === "name") return sign * a.name.localeCompare(b.name);
|
|
130
|
+
const diff = key === "cpu" ? a.cpu - b.cpu : a.ramMB - b.ramMB;
|
|
131
|
+
return diff !== 0 ? sign * diff : a.name.localeCompare(b.name);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Substring match over the app's name, as the toolbar's search box needs it. */
|
|
136
|
+
export function filterAppRows(rows: readonly AppRow[], query: string): AppRow[] {
|
|
137
|
+
const needle = query.trim().toLowerCase();
|
|
138
|
+
if (!needle) return [...rows];
|
|
139
|
+
return rows.filter((r) => r.name.toLowerCase().includes(needle));
|
|
140
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Apps page — Mission Center's Apps section.
|
|
3
|
+
*
|
|
4
|
+
* A row's figures are the sum over the app's whole process SUBTREE, which is why
|
|
5
|
+
* the server sends only the roots and this computes the rest: a Chromium-shaped
|
|
6
|
+
* app is dozens of processes under one root, and a row showing just the root
|
|
7
|
+
* reads as permanently idle.
|
|
8
|
+
*/
|
|
9
|
+
import { useCallback, useMemo, useState } from "react";
|
|
10
|
+
import { Search } from "@/lib/icons";
|
|
11
|
+
import { cn } from "@/lib/utils";
|
|
12
|
+
import { buildAppRows, filterAppRows, sortAppRows, type AppRow as AppRowData, type AppSortKey } from "./app-rows";
|
|
13
|
+
import { AppRow } from "./app-row";
|
|
14
|
+
import { KillConfirmDialog } from "../kill-confirm-dialog";
|
|
15
|
+
import { useProcessKill } from "../use-process-kill";
|
|
16
|
+
import { isGroupProtected } from "../build-kill-request";
|
|
17
|
+
import type { MetricsSnapshot, ProcessGroup, ProcessInfo } from "../../../../types/system-metrics";
|
|
18
|
+
|
|
19
|
+
const SORTS: { key: AppSortKey; label: string }[] = [
|
|
20
|
+
{ key: "cpu", label: "CPU" },
|
|
21
|
+
{ key: "ram", label: "Memory" },
|
|
22
|
+
{ key: "name", label: "Name" },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* An app ended as a group. `rootPid` is null on purpose: an app can have several
|
|
27
|
+
* roots, so there is no single tree kill that covers it — `buildGroupKillRequests`
|
|
28
|
+
* then ends each member individually, which reaches every helper.
|
|
29
|
+
*/
|
|
30
|
+
function groupForApp(app: AppRowData): ProcessGroup {
|
|
31
|
+
return {
|
|
32
|
+
key: `app:${app.id}`,
|
|
33
|
+
label: app.name,
|
|
34
|
+
rootPid: null,
|
|
35
|
+
cpu: app.cpu,
|
|
36
|
+
ramMB: app.ramMB,
|
|
37
|
+
count: app.processCount,
|
|
38
|
+
ppm: false,
|
|
39
|
+
pids: app.memberPids,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function AppsPanel({ snapshot }: { snapshot: MetricsSnapshot }) {
|
|
44
|
+
const [query, setQuery] = useState("");
|
|
45
|
+
const [sortKey, setSortKey] = useState<AppSortKey>("cpu");
|
|
46
|
+
const { pendingKill, requestKillGroup, confirmKill, cancelKill } = useProcessKill(snapshot);
|
|
47
|
+
|
|
48
|
+
const byPid = useMemo(
|
|
49
|
+
() => new Map(snapshot.processes.map((p) => [p.pid, p])),
|
|
50
|
+
[snapshot.processes],
|
|
51
|
+
);
|
|
52
|
+
const rows = useMemo(
|
|
53
|
+
() => buildAppRows(snapshot.apps ?? [], snapshot.processes),
|
|
54
|
+
[snapshot.apps, snapshot.processes],
|
|
55
|
+
);
|
|
56
|
+
const visible = useMemo(
|
|
57
|
+
() => sortAppRows(filterAppRows(rows, query), sortKey, sortKey === "name" ? "asc" : "desc"),
|
|
58
|
+
[rows, query, sortKey],
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
const protectedOf = useCallback(
|
|
62
|
+
(app: AppRowData) => isGroupProtected(
|
|
63
|
+
app.memberPids.map((pid) => byPid.get(pid)).filter((p): p is ProcessInfo => p !== undefined),
|
|
64
|
+
),
|
|
65
|
+
[byPid],
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (snapshot.apps === undefined) {
|
|
69
|
+
return (
|
|
70
|
+
<div className="p-4 text-sm text-text-subtle" data-testid="sysmon-apps">
|
|
71
|
+
This host does not list desktop applications.
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
// `@container` so the Swap column is dropped on the PANEL's width rather
|
|
78
|
+
// than the viewport's — the System Monitor is a floating window on desktop.
|
|
79
|
+
<div className="h-full flex flex-col min-h-0 @container" data-testid="sysmon-apps" data-app-count={visible.length}>
|
|
80
|
+
<div className="shrink-0 flex flex-wrap items-center gap-2 p-2 border-b border-border">
|
|
81
|
+
<div className="relative flex-1 min-w-[140px]">
|
|
82
|
+
<Search className="absolute left-2 top-1/2 -translate-y-1/2 size-3.5 text-text-subtle" />
|
|
83
|
+
<input
|
|
84
|
+
type="text"
|
|
85
|
+
value={query}
|
|
86
|
+
onChange={(e) => setQuery(e.target.value)}
|
|
87
|
+
placeholder="Search applications"
|
|
88
|
+
aria-label="Search applications"
|
|
89
|
+
data-testid="sysmon-app-search"
|
|
90
|
+
className="w-full min-h-11 md:min-h-8 pl-7 pr-2 text-sm rounded-md border border-border bg-transparent focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
91
|
+
/>
|
|
92
|
+
</div>
|
|
93
|
+
{SORTS.map((s) => (
|
|
94
|
+
<button
|
|
95
|
+
key={s.key}
|
|
96
|
+
type="button"
|
|
97
|
+
onClick={() => setSortKey(s.key)}
|
|
98
|
+
aria-pressed={sortKey === s.key}
|
|
99
|
+
data-testid={`sysmon-app-sort-${s.key}`}
|
|
100
|
+
className={cn(
|
|
101
|
+
"min-h-11 md:min-h-8 px-3 text-xs rounded-md border transition-colors",
|
|
102
|
+
sortKey === s.key ? "border-primary bg-primary/10 text-primary" : "border-border hover:bg-surface-hover",
|
|
103
|
+
)}
|
|
104
|
+
>
|
|
105
|
+
{s.label}
|
|
106
|
+
</button>
|
|
107
|
+
))}
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div className="flex-1 min-h-0 overflow-y-auto divide-y divide-border">
|
|
111
|
+
{visible.length === 0 && (
|
|
112
|
+
<p className="p-4 text-sm text-text-subtle">
|
|
113
|
+
{rows.length === 0 ? "No desktop applications are running." : "No applications match."}
|
|
114
|
+
</p>
|
|
115
|
+
)}
|
|
116
|
+
{visible.map((app) => (
|
|
117
|
+
<AppRow
|
|
118
|
+
key={app.id}
|
|
119
|
+
app={app}
|
|
120
|
+
endProtected={protectedOf(app)}
|
|
121
|
+
onEnd={(target) => requestKillGroup(groupForApp(target))}
|
|
122
|
+
/>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<KillConfirmDialog target={pendingKill} onConfirm={confirmKill} onCancel={cancelKill} />
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}
|