@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,116 @@
|
|
|
1
|
+
/** One GPU — Mission Center's GPU page: three graphs (utilisation, video
|
|
2
|
+
* encode/decode, memory usage) over the driver's static facts. Every row here is
|
|
3
|
+
* optional in the contract: a driver that publishes no clock or no power reports
|
|
4
|
+
* nothing rather than zero, so the row shows an em dash. */
|
|
5
|
+
import { formatRam } from "@/lib/format-bytes";
|
|
6
|
+
import { historySpanLabel } from "@/lib/cpu-graph-mode";
|
|
7
|
+
import { formatTemp } from "@/lib/temperature";
|
|
8
|
+
import { useSettingsStore } from "@/stores/settings-store";
|
|
9
|
+
import { CHART_COLORS, DetailChart, DetailHeader, Stat, StatGrid, useSeries } from "./detail-parts";
|
|
10
|
+
import type { GpuInfo } from "../../../../types/system-metrics";
|
|
11
|
+
import type { GpuMetrics, MetricsHistoryPoint } from "../../../../types/system-metrics";
|
|
12
|
+
|
|
13
|
+
function pcie(gen: number | undefined, lanes: number | undefined): string | undefined {
|
|
14
|
+
if (gen === undefined && lanes === undefined) return undefined;
|
|
15
|
+
return `${gen === undefined ? "?" : `Gen ${gen}`} x${lanes ?? "?"}`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function GpuDetail({
|
|
19
|
+
gpu, info, index, history,
|
|
20
|
+
}: { gpu: GpuMetrics; info?: GpuInfo; index: number; history: readonly MetricsHistoryPoint[] }) {
|
|
21
|
+
const tempUnit = useSettingsStore((s) => s.sysmonTempUnit);
|
|
22
|
+
const pick = (p: MetricsHistoryPoint) =>
|
|
23
|
+
p.system.gpus.find((g) => (gpu.id ? g.id === gpu.id : g.name === gpu.name));
|
|
24
|
+
const util = useSeries(history, (p) => pick(p)?.utilPercent);
|
|
25
|
+
const encode = useSeries(history, (p) => pick(p)?.encodePercent);
|
|
26
|
+
const decode = useSeries(history, (p) => pick(p)?.decodePercent);
|
|
27
|
+
// Dedicated memory where the card has some, its share of system RAM where it
|
|
28
|
+
// does not — the two are never both drawn, because they are the same graph
|
|
29
|
+
// answering "how much memory is this GPU using" from whichever source exists.
|
|
30
|
+
const hasVram = gpu.vramTotalMB > 0;
|
|
31
|
+
const hasShared = !hasVram && gpu.sharedTotalMB !== undefined;
|
|
32
|
+
const memory = useSeries(history, (p) => {
|
|
33
|
+
const g = pick(p);
|
|
34
|
+
return hasVram ? g?.vramUsedMB : g?.sharedUsedMB;
|
|
35
|
+
});
|
|
36
|
+
const memoryTotalMB = hasVram ? gpu.vramTotalMB : gpu.sharedTotalMB ?? 0;
|
|
37
|
+
|
|
38
|
+
const hasVideo = gpu.encodePercent !== undefined || gpu.decodePercent !== undefined;
|
|
39
|
+
// Intel runs one engine for both directions, so there is one series with the
|
|
40
|
+
// combined label rather than a decode line that would always trace the encode.
|
|
41
|
+
const videoShared = info?.encodeDecodeShared === true;
|
|
42
|
+
const span = historySpanLabel(history.map((p) => p.ts));
|
|
43
|
+
const caption = (what: string) => (span ? `${what} ${span}` : what);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className="space-y-4" data-testid="sysmon-detail-gpu" data-gpu-id={gpu.id ?? index}>
|
|
47
|
+
<DetailHeader title={`GPU ${index}`} subtitle={gpu.name} value={`${gpu.utilPercent.toFixed(0)}%`} />
|
|
48
|
+
|
|
49
|
+
<DetailChart
|
|
50
|
+
grid
|
|
51
|
+
maxValue={100}
|
|
52
|
+
caption={caption("Utilisation")}
|
|
53
|
+
ceiling="100%"
|
|
54
|
+
series={[{ data: util, color: CHART_COLORS.primary }]}
|
|
55
|
+
/>
|
|
56
|
+
|
|
57
|
+
{hasVideo && (
|
|
58
|
+
<DetailChart
|
|
59
|
+
grid
|
|
60
|
+
height={90}
|
|
61
|
+
maxValue={100}
|
|
62
|
+
caption={caption(videoShared ? "Video encode/decode utilisation" : "Video encode and decode utilisation")}
|
|
63
|
+
ceiling="100%"
|
|
64
|
+
legend={videoShared ? undefined : ["Encode", "Decode"]}
|
|
65
|
+
series={videoShared
|
|
66
|
+
? [{ data: encode, color: CHART_COLORS.secondary }]
|
|
67
|
+
: [
|
|
68
|
+
{ data: encode, color: CHART_COLORS.secondary },
|
|
69
|
+
{ data: decode, color: CHART_COLORS.error },
|
|
70
|
+
]}
|
|
71
|
+
/>
|
|
72
|
+
)}
|
|
73
|
+
|
|
74
|
+
{(hasVram || hasShared) && (
|
|
75
|
+
<DetailChart
|
|
76
|
+
grid
|
|
77
|
+
height={90}
|
|
78
|
+
maxValue={memoryTotalMB}
|
|
79
|
+
caption={caption(hasVram ? "Video memory usage" : "Memory usage")}
|
|
80
|
+
ceiling={formatRam(memoryTotalMB)}
|
|
81
|
+
series={[{ data: memory, color: CHART_COLORS.success }]}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
|
|
85
|
+
<StatGrid>
|
|
86
|
+
<Stat label="Video memory" value={hasVram
|
|
87
|
+
? `${formatRam(gpu.vramUsedMB)} / ${formatRam(gpu.vramTotalMB)}` : undefined} />
|
|
88
|
+
<Stat label={hasVram ? "Shared memory" : "Memory usage"} value={gpu.sharedTotalMB === undefined ? undefined
|
|
89
|
+
: `${formatRam(gpu.sharedUsedMB ?? 0)} / ${formatRam(gpu.sharedTotalMB)}`} />
|
|
90
|
+
<Stat
|
|
91
|
+
label={videoShared ? "Video encode/decode" : "Video encode"}
|
|
92
|
+
value={gpu.encodePercent === undefined ? undefined : `${gpu.encodePercent.toFixed(0)}%`}
|
|
93
|
+
/>
|
|
94
|
+
{!videoShared && (
|
|
95
|
+
<Stat label="Video decode" value={gpu.decodePercent === undefined ? undefined
|
|
96
|
+
: `${gpu.decodePercent.toFixed(0)}%`} />
|
|
97
|
+
)}
|
|
98
|
+
<Stat label="Clock" value={gpu.clockMHz === undefined ? undefined
|
|
99
|
+
: `${gpu.clockMHz} MHz${gpu.clockMaxMHz ? ` / ${gpu.clockMaxMHz}` : ""}`} />
|
|
100
|
+
<Stat label="Memory clock" value={gpu.memClockMHz === undefined ? undefined
|
|
101
|
+
: `${gpu.memClockMHz} MHz${gpu.memClockMaxMHz ? ` / ${gpu.memClockMaxMHz}` : ""}`} />
|
|
102
|
+
<Stat label="Power" value={gpu.powerW === undefined ? undefined
|
|
103
|
+
: `${gpu.powerW.toFixed(1)} W${gpu.powerMaxW ? ` / ${gpu.powerMaxW}` : ""}`} />
|
|
104
|
+
<Stat label="Temperature" value={formatTemp(gpu.tempC, tempUnit)} />
|
|
105
|
+
<Stat label="Vendor" value={info?.vendor} />
|
|
106
|
+
<Stat label="Driver" value={info?.driver === undefined ? undefined
|
|
107
|
+
: [info.driver, info.driverVersion].filter(Boolean).join(" ")} />
|
|
108
|
+
<Stat label="OpenGL" value={info?.openglVersion} />
|
|
109
|
+
<Stat label="Vulkan" value={info?.vulkanVersion} />
|
|
110
|
+
<Stat label="PCI bus address" value={gpu.id} />
|
|
111
|
+
<Stat label="PCIe link" value={pcie(info?.pcieGen, info?.pcieLanes)} />
|
|
112
|
+
<Stat label="PCIe maximum" value={pcie(info?.pcieMaxGen, info?.pcieMaxLanes)} />
|
|
113
|
+
</StatGrid>
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/** Memory page — Mission Center's Memory tab: the usage graph, the composition
|
|
2
|
+
* strip whose four parts add up to the total exactly, and the SMBIOS slot table. */
|
|
3
|
+
import { formatBytes, formatRam } from "@/lib/format-bytes";
|
|
4
|
+
import { historySpanLabel } from "@/lib/cpu-graph-mode";
|
|
5
|
+
import { CHART_COLORS, CompositionBar, DetailChart, DetailHeader, Stat, StatGrid, useSeries } from "./detail-parts";
|
|
6
|
+
import type { MemoryInfo } from "../../../../types/system-hardware";
|
|
7
|
+
import type { MemoryMetrics, MetricsHistoryPoint } from "../../../../types/system-metrics";
|
|
8
|
+
|
|
9
|
+
export function MemoryDetail({
|
|
10
|
+
mem, info, history,
|
|
11
|
+
}: { mem: MemoryMetrics; info?: MemoryInfo; history: readonly MetricsHistoryPoint[] }) {
|
|
12
|
+
const used = useSeries(history, (p) => p.system.mem.percent);
|
|
13
|
+
// Percent of the swap that is in use, so the graph shares the utilisation axis
|
|
14
|
+
// with the memory one above it rather than autoscaling to whatever megabyte
|
|
15
|
+
// figure happens to be the window's maximum.
|
|
16
|
+
const swap = useSeries(history, (p) => {
|
|
17
|
+
const total = p.system.mem.swapTotalMB ?? 0;
|
|
18
|
+
return total > 0 ? ((p.system.mem.swapUsedMB ?? 0) / total) * 100 : 0;
|
|
19
|
+
});
|
|
20
|
+
// The four byte figures are produced together or not at all, so one of them
|
|
21
|
+
// being present is enough to know the strip can be drawn.
|
|
22
|
+
const composed = mem.inUseBytes !== undefined;
|
|
23
|
+
const span = historySpanLabel(history.map((p) => p.ts));
|
|
24
|
+
const caption = (what: string) => (span ? `${what} ${span}` : what);
|
|
25
|
+
// A host with swap off gets no swap graph at all: a flat zero line over an
|
|
26
|
+
// axis that does not exist reads as "nothing is swapping", which is a
|
|
27
|
+
// different claim from "there is nowhere to swap to".
|
|
28
|
+
const hasSwap = (mem.swapTotalMB ?? 0) > 0;
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div className="space-y-4" data-testid="sysmon-detail-memory">
|
|
32
|
+
<DetailHeader
|
|
33
|
+
title="Memory"
|
|
34
|
+
subtitle={`${formatRam(mem.usedMB)} of ${formatRam(mem.totalMB)} in use`}
|
|
35
|
+
value={`${mem.percent.toFixed(1)}%`}
|
|
36
|
+
/>
|
|
37
|
+
<DetailChart
|
|
38
|
+
grid
|
|
39
|
+
maxValue={100}
|
|
40
|
+
caption={caption("Memory usage")}
|
|
41
|
+
ceiling={formatRam(mem.totalMB)}
|
|
42
|
+
series={[{ data: used, color: CHART_COLORS.primary }]}
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
{hasSwap && (
|
|
46
|
+
<DetailChart
|
|
47
|
+
grid
|
|
48
|
+
height={90}
|
|
49
|
+
maxValue={100}
|
|
50
|
+
caption={caption("Swap usage")}
|
|
51
|
+
ceiling={formatRam(mem.swapTotalMB ?? 0)}
|
|
52
|
+
series={[{ data: swap, color: CHART_COLORS.success }]}
|
|
53
|
+
/>
|
|
54
|
+
)}
|
|
55
|
+
|
|
56
|
+
{composed && (
|
|
57
|
+
<div className="space-y-2">
|
|
58
|
+
<CompositionBar segments={[
|
|
59
|
+
{ label: "In use", value: mem.inUseBytes ?? 0, color: "var(--color-primary)" },
|
|
60
|
+
{ label: "Modified", value: mem.modifiedBytes ?? 0, color: "var(--color-warning)" },
|
|
61
|
+
{ label: "Standby", value: mem.standbyBytes ?? 0, color: "var(--color-success)" },
|
|
62
|
+
{ label: "Free", value: mem.freeBytes ?? 0, color: "var(--color-border)" },
|
|
63
|
+
]} />
|
|
64
|
+
<div className="flex flex-wrap gap-x-4 gap-y-1 text-[11px] text-text-subtle">
|
|
65
|
+
<span>In use {formatBytes(mem.inUseBytes ?? 0)}</span>
|
|
66
|
+
<span>Modified {formatBytes(mem.modifiedBytes ?? 0)}</span>
|
|
67
|
+
<span>Standby {formatBytes(mem.standbyBytes ?? 0)}</span>
|
|
68
|
+
<span>Free {formatBytes(mem.freeBytes ?? 0)}</span>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
)}
|
|
72
|
+
|
|
73
|
+
<StatGrid>
|
|
74
|
+
<Stat label="Available" value={formatRam(mem.availableMB)} />
|
|
75
|
+
<Stat label="Cached" value={mem.cachedMB === undefined ? undefined : formatRam(mem.cachedMB)} />
|
|
76
|
+
<Stat label="Committed" value={mem.committedMB === undefined ? undefined
|
|
77
|
+
: `${formatRam(mem.committedMB)} / ${mem.commitLimitMB === undefined ? "?" : formatRam(mem.commitLimitMB)}`} />
|
|
78
|
+
<Stat label="Swap" value={mem.swapTotalMB === undefined ? undefined
|
|
79
|
+
: `${formatRam(mem.swapUsedMB ?? 0)} / ${formatRam(mem.swapTotalMB)}`} />
|
|
80
|
+
<Stat label="Compressed (zram)" value={mem.zramCompressedMB === undefined ? undefined
|
|
81
|
+
: formatRam(mem.zramCompressedMB)} />
|
|
82
|
+
<Stat label="Savings (zram)" value={mem.zramSavingsMB === undefined ? undefined
|
|
83
|
+
: formatRam(mem.zramSavingsMB)} />
|
|
84
|
+
<Stat label="Slots used" value={info === undefined ? undefined
|
|
85
|
+
: `${info.devices.length}${info.slotsTotal ? ` of ${info.slotsTotal}` : ""}`} />
|
|
86
|
+
<Stat label="Maximum capacity" value={info?.maxCapacityBytes === undefined ? undefined
|
|
87
|
+
: formatBytes(info.maxCapacityBytes)} />
|
|
88
|
+
</StatGrid>
|
|
89
|
+
|
|
90
|
+
{info && info.devices.length > 0 && (
|
|
91
|
+
<div className="space-y-1">
|
|
92
|
+
<h4 className="text-xs font-medium text-text-secondary">Slots</h4>
|
|
93
|
+
<div className="rounded-md border border-border divide-y divide-border">
|
|
94
|
+
{info.devices.map((device) => (
|
|
95
|
+
<div key={device.locator} className="flex items-center justify-between gap-3 px-3 py-2 text-xs">
|
|
96
|
+
<span className="font-medium shrink-0">{device.locator}</span>
|
|
97
|
+
<span className="text-text-subtle truncate">
|
|
98
|
+
{[device.manufacturer, device.ramType, device.formFactor,
|
|
99
|
+
device.speedMts ? `${device.speedMts} MT/s` : null].filter(Boolean).join(" · ")}
|
|
100
|
+
</span>
|
|
101
|
+
<span className="tabular-nums shrink-0">{formatBytes(device.sizeBytes)}</span>
|
|
102
|
+
</div>
|
|
103
|
+
))}
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
)}
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** One network interface — Mission Center's per-interface page. */
|
|
2
|
+
import { formatBps, formatBytes } from "@/lib/format-bytes";
|
|
3
|
+
import { CHART_COLORS, DetailChart, DetailHeader, Stat, StatGrid, useSeries } from "./detail-parts";
|
|
4
|
+
import { NIC_KIND_LABELS } from "./device-list";
|
|
5
|
+
import type { NicInfo } from "../../../../types/system-hardware";
|
|
6
|
+
import type { MetricsHistoryPoint, NicMetrics, NicState } from "../../../../types/system-metrics";
|
|
7
|
+
|
|
8
|
+
const STATE_LABELS: Record<NicState, string> = {
|
|
9
|
+
connected: "Connected",
|
|
10
|
+
connecting: "Connecting",
|
|
11
|
+
disconnected: "Disconnected",
|
|
12
|
+
unavailable: "Unavailable",
|
|
13
|
+
unknown: "Unknown",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function NicDetail({
|
|
17
|
+
nic, info, history,
|
|
18
|
+
}: { nic: NicMetrics; info?: NicInfo; history: readonly MetricsHistoryPoint[] }) {
|
|
19
|
+
const pick = (p: MetricsHistoryPoint) => p.system.nics?.find((n) => n.id === nic.id);
|
|
20
|
+
const rx = useSeries(history, (p) => pick(p)?.rxBps);
|
|
21
|
+
const tx = useSeries(history, (p) => pick(p)?.txBps);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className="space-y-4" data-testid="sysmon-detail-nic" data-nic-id={nic.id}>
|
|
25
|
+
<DetailHeader
|
|
26
|
+
title={NIC_KIND_LABELS[info?.kind ?? "other"]}
|
|
27
|
+
subtitle={[nic.id, info?.deviceName].filter(Boolean).join(" · ")}
|
|
28
|
+
value={nic.available ? formatBps(nic.rxBps + nic.txBps) : "—"}
|
|
29
|
+
/>
|
|
30
|
+
<DetailChart
|
|
31
|
+
legend={["Receive", "Send"]}
|
|
32
|
+
series={[
|
|
33
|
+
{ data: rx, color: CHART_COLORS.primary },
|
|
34
|
+
{ data: tx, color: CHART_COLORS.secondary },
|
|
35
|
+
]}
|
|
36
|
+
/>
|
|
37
|
+
<StatGrid>
|
|
38
|
+
<Stat label="State" value={STATE_LABELS[nic.state]} />
|
|
39
|
+
<Stat label="Receive" value={nic.available ? formatBps(nic.rxBps) : undefined} />
|
|
40
|
+
<Stat label="Send" value={nic.available ? formatBps(nic.txBps) : undefined} />
|
|
41
|
+
<Stat label="Maximum bitrate" value={nic.linkMbps === undefined ? undefined : `${nic.linkMbps} Mbps`} />
|
|
42
|
+
<Stat label="Total received" value={formatBytes(nic.rxTotal)} />
|
|
43
|
+
<Stat label="Total sent" value={formatBytes(nic.txTotal)} />
|
|
44
|
+
<Stat label="Network name" value={nic.ssid} />
|
|
45
|
+
<Stat label="Signal" value={nic.signalPercent === undefined ? undefined : `${nic.signalPercent}%`} />
|
|
46
|
+
<Stat label="Frequency" value={nic.frequencyMHz === undefined ? undefined
|
|
47
|
+
: `${(nic.frequencyMHz / 1000).toFixed(1)} GHz`} />
|
|
48
|
+
<Stat label="Driver" value={info?.driver} />
|
|
49
|
+
<Stat label="Hardware address" value={info?.mac} />
|
|
50
|
+
<Stat label="IPv4" value={info?.ipv4.join(", ")} title={info?.ipv4.join("\n")} />
|
|
51
|
+
<Stat label="IPv6" value={info?.ipv6.join(", ")} title={info?.ipv6.join("\n")} />
|
|
52
|
+
</StatGrid>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A drive's partitions — Mission Center's Partitions section.
|
|
3
|
+
*
|
|
4
|
+
* One row per partition, not per mount: a partition's usage belongs to its
|
|
5
|
+
* FILESYSTEM, and a btrfs subvolume layout mounts one filesystem at seven places
|
|
6
|
+
* that all report the same figures. Seven identical bars would be noise, so the
|
|
7
|
+
* mount points are listed together above one bar.
|
|
8
|
+
*/
|
|
9
|
+
import { formatBytes } from "@/lib/format-bytes";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
import type { PartitionInfo } from "../../../../types/system-hardware";
|
|
12
|
+
|
|
13
|
+
/** Above this a bar earns a warning colour — a filesystem this full is a problem
|
|
14
|
+
* worth noticing before it is one. */
|
|
15
|
+
const FULL_PERCENT = 90;
|
|
16
|
+
const BUSY_PERCENT = 75;
|
|
17
|
+
|
|
18
|
+
function barColor(percent: number): string {
|
|
19
|
+
if (percent >= FULL_PERCENT) return "bg-error";
|
|
20
|
+
if (percent >= BUSY_PERCENT) return "bg-warning";
|
|
21
|
+
return "bg-primary";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function PartitionList({ partitions }: { partitions: readonly PartitionInfo[] }) {
|
|
25
|
+
if (partitions.length === 0) return null;
|
|
26
|
+
return (
|
|
27
|
+
<section className="space-y-2" data-testid="sysmon-partitions">
|
|
28
|
+
<h4 className="text-sm font-medium">Partitions</h4>
|
|
29
|
+
<ul className="space-y-2">
|
|
30
|
+
{partitions.map((part) => (
|
|
31
|
+
<PartitionRow key={part.id} part={part} />
|
|
32
|
+
))}
|
|
33
|
+
</ul>
|
|
34
|
+
</section>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function PartitionRow({ part }: { part: PartitionInfo }) {
|
|
39
|
+
// The first mount that answered: every mount of one filesystem reports the
|
|
40
|
+
// same statfs, so there is nothing to choose between them.
|
|
41
|
+
const usage = part.mounts.find((m) => m.usedBytes !== undefined && m.totalBytes);
|
|
42
|
+
const percent = usage ? Math.min(100, (usage.usedBytes! / usage.totalBytes!) * 100) : undefined;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<li className="rounded-md border border-border px-3 py-2 space-y-1.5" data-testid="sysmon-partition">
|
|
46
|
+
<div className="flex items-baseline justify-between gap-3">
|
|
47
|
+
<span className="text-sm font-medium truncate" title={part.devicePath}>{part.devicePath}</span>
|
|
48
|
+
<span className="text-[11px] text-text-subtle shrink-0 tabular-nums">
|
|
49
|
+
{part.filesystem ?? "—"} · {formatBytes(part.sizeBytes)}
|
|
50
|
+
</span>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
{part.mounts.length === 0 ? (
|
|
54
|
+
<p className="text-[11px] text-text-subtle">Not mounted</p>
|
|
55
|
+
) : (
|
|
56
|
+
<>
|
|
57
|
+
<p className="text-[11px] text-text-subtle truncate" title={part.mounts.map((m) => m.mountPoint).join(", ")}>
|
|
58
|
+
{part.mounts.map((m) => m.mountPoint).join(", ")}
|
|
59
|
+
</p>
|
|
60
|
+
{percent === undefined ? (
|
|
61
|
+
// Mounted but statfs refused. An unreadable filesystem is not an
|
|
62
|
+
// empty one, so it gets no bar rather than a bar at zero.
|
|
63
|
+
<p className="text-[11px] text-text-subtle">Usage unavailable</p>
|
|
64
|
+
) : (
|
|
65
|
+
<div className="space-y-1">
|
|
66
|
+
<div className="h-1.5 w-full rounded-full bg-surface-hover overflow-hidden">
|
|
67
|
+
<div className={cn("h-full rounded-full", barColor(percent))} style={{ width: `${percent}%` }} />
|
|
68
|
+
</div>
|
|
69
|
+
<div className="flex items-baseline justify-between gap-2 text-[11px] text-text-subtle tabular-nums">
|
|
70
|
+
<span>{formatBytes(usage!.usedBytes!)} of {formatBytes(usage!.totalBytes!)}</span>
|
|
71
|
+
<span>{percent.toFixed(0)}%</span>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
</>
|
|
76
|
+
)}
|
|
77
|
+
</li>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Performance page — Mission Center's device sidebar plus one detail page.
|
|
3
|
+
*
|
|
4
|
+
* Two presentations from one tree: at `md` and above the sidebar and the detail
|
|
5
|
+
* sit side by side; below it the sidebar IS the page and tapping a device drills
|
|
6
|
+
* into it, because a 200px sidebar beside a detail grid on a phone leaves neither
|
|
7
|
+
* of them usable.
|
|
8
|
+
*/
|
|
9
|
+
import { useMemo, type ComponentType } from "react";
|
|
10
|
+
import { Activity, ChevronLeft, ChevronRight, Cpu, HardDrive, Layers, Monitor, Wifi } from "@/lib/icons";
|
|
11
|
+
import { cn } from "@/lib/utils";
|
|
12
|
+
import { useIsMobile } from "@/hooks/use-is-mobile";
|
|
13
|
+
import { buildDeviceList, resolveSelected, type DeviceEntry, type DeviceKind } from "./device-list";
|
|
14
|
+
import { useHardwareInventory } from "./use-hardware-inventory";
|
|
15
|
+
import { CpuDetail } from "./cpu-detail";
|
|
16
|
+
import { MemoryDetail } from "./memory-detail";
|
|
17
|
+
import { DiskDetail } from "./disk-detail";
|
|
18
|
+
import { NicDetail } from "./nic-detail";
|
|
19
|
+
import { GpuDetail } from "./gpu-detail";
|
|
20
|
+
import { FansDetail } from "./fans-detail";
|
|
21
|
+
import type { HardwareInventory } from "../../../../types/system-hardware";
|
|
22
|
+
import type { MetricsHistoryPoint, SystemMetrics } from "../../../../types/system-metrics";
|
|
23
|
+
|
|
24
|
+
const KIND_ICONS: Record<DeviceKind, ComponentType<{ className?: string }>> = {
|
|
25
|
+
cpu: Cpu, memory: Layers, disk: HardDrive, nic: Wifi, gpu: Monitor, fans: Activity,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function barColor(percent: number): string {
|
|
29
|
+
if (percent > 80) return "bg-error";
|
|
30
|
+
if (percent > 50) return "bg-warning";
|
|
31
|
+
return "bg-primary";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function DeviceRow({
|
|
35
|
+
entry, selected, showChevron, onSelect,
|
|
36
|
+
}: { entry: DeviceEntry; selected: boolean; showChevron: boolean; onSelect: (key: string) => void }) {
|
|
37
|
+
const Icon = KIND_ICONS[entry.kind];
|
|
38
|
+
return (
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
41
|
+
onClick={() => onSelect(entry.key)}
|
|
42
|
+
aria-current={selected}
|
|
43
|
+
data-testid="sysmon-device-row"
|
|
44
|
+
data-device-key={entry.key}
|
|
45
|
+
className={cn(
|
|
46
|
+
"w-full min-h-11 px-3 py-2 flex items-center gap-3 text-left transition-colors",
|
|
47
|
+
selected ? "bg-primary/10 text-text-primary" : "hover:bg-surface-hover",
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
<Icon className="size-4 shrink-0 text-text-subtle" />
|
|
51
|
+
<span className="flex-1 min-w-0">
|
|
52
|
+
<span className="block text-sm truncate">{entry.label}</span>
|
|
53
|
+
<span className="block text-[11px] text-text-subtle truncate" title={entry.sublabel}>
|
|
54
|
+
{entry.sublabel}
|
|
55
|
+
</span>
|
|
56
|
+
{/* No bar at all when the device has no percentage — an empty track would
|
|
57
|
+
read as "measured, and it is zero". */}
|
|
58
|
+
{entry.percent !== null && (
|
|
59
|
+
<span className="mt-1 block h-1 w-full rounded-full bg-surface-hover overflow-hidden">
|
|
60
|
+
<span
|
|
61
|
+
className={cn("block h-full transition-[width]", barColor(entry.percent))}
|
|
62
|
+
style={{ width: `${Math.max(1, Math.min(100, entry.percent))}%` }}
|
|
63
|
+
/>
|
|
64
|
+
</span>
|
|
65
|
+
)}
|
|
66
|
+
</span>
|
|
67
|
+
<span className="text-xs tabular-nums text-text-secondary shrink-0">{entry.detail}</span>
|
|
68
|
+
{showChevron && <ChevronRight className="size-4 shrink-0 text-text-subtle" />}
|
|
69
|
+
</button>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function DeviceDetail({
|
|
74
|
+
entry, system, inventory, history,
|
|
75
|
+
}: {
|
|
76
|
+
entry: DeviceEntry;
|
|
77
|
+
system: SystemMetrics;
|
|
78
|
+
inventory: HardwareInventory | null;
|
|
79
|
+
history: readonly MetricsHistoryPoint[];
|
|
80
|
+
}) {
|
|
81
|
+
switch (entry.kind) {
|
|
82
|
+
case "cpu":
|
|
83
|
+
return <CpuDetail cpu={system.cpu} info={inventory?.cpu} history={history} processCount={system.processCount} />;
|
|
84
|
+
case "memory":
|
|
85
|
+
return <MemoryDetail mem={system.mem} info={inventory?.memory} history={history} />;
|
|
86
|
+
case "disk": {
|
|
87
|
+
const index = (system.disks ?? []).findIndex((d) => d.id === entry.id);
|
|
88
|
+
const disk = index >= 0 ? system.disks?.[index] : undefined;
|
|
89
|
+
if (!disk) return null;
|
|
90
|
+
return (
|
|
91
|
+
<DiskDetail
|
|
92
|
+
disk={disk}
|
|
93
|
+
index={index}
|
|
94
|
+
info={inventory?.disks.find((d) => d.id === disk.id)}
|
|
95
|
+
history={history}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
case "nic": {
|
|
100
|
+
const nic = (system.nics ?? []).find((n) => n.id === entry.id);
|
|
101
|
+
if (!nic) return null;
|
|
102
|
+
return <NicDetail nic={nic} info={inventory?.nics.find((n) => n.id === nic.id)} history={history} />;
|
|
103
|
+
}
|
|
104
|
+
case "gpu": {
|
|
105
|
+
const index = system.gpus.findIndex((g) => (entry.id ? g.id === entry.id : true));
|
|
106
|
+
const gpu = index >= 0 ? system.gpus[index] : undefined;
|
|
107
|
+
if (!gpu) return null;
|
|
108
|
+
return (
|
|
109
|
+
<GpuDetail
|
|
110
|
+
gpu={gpu}
|
|
111
|
+
index={index}
|
|
112
|
+
info={inventory?.gpus.find((g) => g.id === gpu.id)}
|
|
113
|
+
history={history}
|
|
114
|
+
/>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
case "fans":
|
|
118
|
+
return <FansDetail fans={system.fans ?? []} />;
|
|
119
|
+
default:
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface PerformancePanelProps {
|
|
125
|
+
system: SystemMetrics;
|
|
126
|
+
history: MetricsHistoryPoint[];
|
|
127
|
+
/** Selected device key, owned by the parent so an Overview card can jump
|
|
128
|
+
* straight to one. `null` means "nothing chosen": the desktop sidebar then
|
|
129
|
+
* falls back to the first entry and the phone shows the device list. */
|
|
130
|
+
device: string | null;
|
|
131
|
+
onDeviceChange: (key: string | null) => void;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function PerformancePanel({
|
|
135
|
+
system, history, device, onDeviceChange,
|
|
136
|
+
}: PerformancePanelProps) {
|
|
137
|
+
const isMobile = useIsMobile();
|
|
138
|
+
|
|
139
|
+
// Derived from the tick rather than from the device list, so the inventory
|
|
140
|
+
// fetch does not depend on the list that depends on the inventory.
|
|
141
|
+
const deviceIds = useMemo(
|
|
142
|
+
() => [
|
|
143
|
+
...(system.disks ?? []).map((d) => d.id),
|
|
144
|
+
...(system.nics ?? []).map((n) => n.id),
|
|
145
|
+
...system.gpus.map((g) => g.id).filter((id): id is string => !!id),
|
|
146
|
+
].join(","),
|
|
147
|
+
[system.disks, system.nics, system.gpus],
|
|
148
|
+
);
|
|
149
|
+
const inventory = useHardwareInventory(deviceIds);
|
|
150
|
+
const entries = useMemo(() => buildDeviceList(system, inventory), [system, inventory]);
|
|
151
|
+
|
|
152
|
+
// A device that disappears (a drive unplugged) falls back rather than leaving
|
|
153
|
+
// the page blank.
|
|
154
|
+
const selectedKey = resolveSelected(entries, device);
|
|
155
|
+
const selected = entries.find((e) => e.key === selectedKey);
|
|
156
|
+
|
|
157
|
+
const list = (
|
|
158
|
+
<div className="divide-y divide-border" role="list" data-testid="sysmon-device-list">
|
|
159
|
+
{entries.map((entry) => (
|
|
160
|
+
<DeviceRow
|
|
161
|
+
key={entry.key}
|
|
162
|
+
entry={entry}
|
|
163
|
+
selected={!isMobile && entry.key === selectedKey}
|
|
164
|
+
showChevron={isMobile}
|
|
165
|
+
onSelect={onDeviceChange}
|
|
166
|
+
/>
|
|
167
|
+
))}
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
if (isMobile) {
|
|
172
|
+
// The phone shows one thing at a time, so a key that resolves to something
|
|
173
|
+
// OTHER than what was asked for goes back to the list rather than drilling
|
|
174
|
+
// into the fallback. On desktop that fallback is harmless — the sidebar is on
|
|
175
|
+
// screen saying which device is current — and here there is nothing to say it.
|
|
176
|
+
if (device === null || !selected || selectedKey !== device) {
|
|
177
|
+
return <div className="h-full overflow-y-auto" data-testid="sysmon-performance">{list}</div>;
|
|
178
|
+
}
|
|
179
|
+
return (
|
|
180
|
+
<div className="h-full flex flex-col" data-testid="sysmon-performance">
|
|
181
|
+
<button
|
|
182
|
+
type="button"
|
|
183
|
+
onClick={() => onDeviceChange(null)}
|
|
184
|
+
className="shrink-0 min-h-11 px-3 flex items-center gap-2 text-sm border-b border-border"
|
|
185
|
+
>
|
|
186
|
+
<ChevronLeft className="size-4" />
|
|
187
|
+
Devices
|
|
188
|
+
</button>
|
|
189
|
+
<div className="flex-1 min-h-0 overflow-y-auto p-4 @container">
|
|
190
|
+
<DeviceDetail entry={selected} system={system} inventory={inventory} history={history} />
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<div className="h-full flex min-h-0" data-testid="sysmon-performance">
|
|
198
|
+
<div className="w-56 shrink-0 border-r border-border overflow-y-auto">{list}</div>
|
|
199
|
+
<div className="flex-1 min-w-0 overflow-y-auto p-4 @container">
|
|
200
|
+
{selected && (
|
|
201
|
+
<DeviceDetail entry={selected} system={system} inventory={inventory} history={history} />
|
|
202
|
+
)}
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static device facts for the Performance page.
|
|
3
|
+
*
|
|
4
|
+
* Fetched once, then again only when a snapshot names a device id the inventory
|
|
5
|
+
* has never heard of — a USB drive plugged in, an interface created. It is a
|
|
6
|
+
* ~2.8 KB payload of things that do not change tick to tick (models, capacities,
|
|
7
|
+
* MAC addresses, DIMM layout), so putting it on the 2 s stream would be pure
|
|
8
|
+
* waste over a tunnel.
|
|
9
|
+
*/
|
|
10
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
11
|
+
import { api } from "@/lib/api-client";
|
|
12
|
+
import type { HardwareInventory } from "../../../../types/system-hardware";
|
|
13
|
+
|
|
14
|
+
/** `deviceIds` is a JOINED string, not an array: an array literal is a fresh
|
|
15
|
+
* identity every render, which would turn the refetch effect into a loop. */
|
|
16
|
+
export function useHardwareInventory(deviceIds: string): HardwareInventory | null {
|
|
17
|
+
const [inventory, setInventory] = useState<HardwareInventory | null>(null);
|
|
18
|
+
const inFlight = useRef(false);
|
|
19
|
+
|
|
20
|
+
const load = useCallback(async () => {
|
|
21
|
+
if (inFlight.current) return;
|
|
22
|
+
inFlight.current = true;
|
|
23
|
+
try {
|
|
24
|
+
setInventory(await api.get<HardwareInventory>("/api/system/hardware"));
|
|
25
|
+
} catch {
|
|
26
|
+
// The pages degrade to the ids the tick already carries rather than
|
|
27
|
+
// showing an error over a whole page of otherwise-live figures.
|
|
28
|
+
} finally {
|
|
29
|
+
inFlight.current = false;
|
|
30
|
+
}
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
void load();
|
|
35
|
+
}, [load]);
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!inventory) return;
|
|
39
|
+
const known = new Set<string>([
|
|
40
|
+
...inventory.disks.map((d) => d.id),
|
|
41
|
+
...inventory.nics.map((n) => n.id),
|
|
42
|
+
...inventory.gpus.map((g) => g.id),
|
|
43
|
+
]);
|
|
44
|
+
if (deviceIds.split(",").filter(Boolean).some((id) => !known.has(id))) void load();
|
|
45
|
+
}, [inventory, deviceIds, load]);
|
|
46
|
+
|
|
47
|
+
return inventory;
|
|
48
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { CSSProperties } from "react";
|
|
10
10
|
import type { ProcessColumnAvailability, SortKey } from "../../../types/system-metrics";
|
|
11
11
|
|
|
12
|
-
export type OptionalColumnKey = "disk" | "gpu" | "net";
|
|
12
|
+
export type OptionalColumnKey = "swap" | "disk" | "gpu" | "net";
|
|
13
13
|
/** Every column whose width the user can drag. The name column takes the rest. */
|
|
14
14
|
export type ResizableColumnKey = "cpu" | "ram" | OptionalColumnKey;
|
|
15
15
|
|
|
@@ -20,13 +20,15 @@ export type ProcessColumnsFlags = ProcessColumnAvailability;
|
|
|
20
20
|
/** px overrides from a drag; missing keys fall back to `DEFAULT_COLUMN_WIDTH`. */
|
|
21
21
|
export type ColumnWidths = Partial<Record<ResizableColumnKey, number>>;
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
/** Mission Center's own order on both its tables: Memory, Swap, then the rest. */
|
|
24
|
+
const OPTIONAL_COLUMN_ORDER: OptionalColumnKey[] = ["swap", "disk", "gpu", "net"];
|
|
24
25
|
|
|
25
26
|
/** Disk/Net cells show two values ("↓ read ↑ write"); GPU shows one composite
|
|
26
27
|
* ("12% · 1.1 GB") and fits narrower. */
|
|
27
28
|
export const DEFAULT_COLUMN_WIDTH: Record<ResizableColumnKey, number> = {
|
|
28
29
|
cpu: 64,
|
|
29
30
|
ram: 80,
|
|
31
|
+
swap: 80,
|
|
30
32
|
disk: 120,
|
|
31
33
|
gpu: 90,
|
|
32
34
|
net: 120,
|
|
@@ -54,7 +56,7 @@ export interface ProcessGridResult {
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
function isOptionalColumnKey(key: SortKey): key is OptionalColumnKey {
|
|
57
|
-
return key === "disk" || key === "gpu" || key === "net";
|
|
59
|
+
return key === "swap" || key === "disk" || key === "gpu" || key === "net";
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export function clampColumnWidth(px: number): number {
|