@hienlh/ppm 0.23.5 → 0.23.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +89 -0
- package/CLAUDE.md +21 -1
- package/Dockerfile.test +8 -1
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +30 -1
- package/dist/web/assets/accounts-settings-section-BYWnY9hH.js +1 -0
- package/dist/web/assets/accounts-settings-section-BYWnY9hH.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-BYWnY9hH.js.gz +0 -0
- package/dist/web/assets/{ai-resource-editor-DKcsVDle.js → ai-resource-editor-CY4n_Ccv.js} +1 -1
- package/dist/web/assets/ai-resource-editor-CY4n_Ccv.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-CY4n_Ccv.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-Bs3iUW4j.js +1 -0
- package/dist/web/assets/ai-settings-section-Bs3iUW4j.js.br +0 -0
- package/dist/web/assets/ai-settings-section-Bs3iUW4j.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-DDs11XV4.js +1 -0
- package/dist/web/assets/api-client-CJPnCOG-.js +1 -0
- package/dist/web/assets/{api-client-c8YOpTlT.js → api-client-D-SMbdiH.js} +1 -1
- package/dist/web/assets/api-client-D-SMbdiH.js.br +0 -0
- package/dist/web/assets/api-client-D-SMbdiH.js.gz +0 -0
- package/dist/web/assets/api-settings-BDkkhFMq.js +1 -0
- package/dist/web/assets/{api-settings-BmvNoCbn.js → api-settings-Bv9mLCty.js} +1 -1
- package/dist/web/assets/api-settings-Bv9mLCty.js.br +0 -0
- package/dist/web/assets/api-settings-Bv9mLCty.js.gz +0 -0
- package/dist/web/assets/{api-themes-6fOQJgM9.js → api-themes-3wW-zLzU.js} +1 -1
- package/dist/web/assets/appearance-settings-section-C0BoIBmz.js +1 -0
- package/dist/web/assets/appearance-settings-section-C0BoIBmz.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-C0BoIBmz.js.gz +0 -0
- package/dist/web/assets/apps-panel-DXAFsveZ.js +1 -0
- package/dist/web/assets/apps-panel-DXAFsveZ.js.br +0 -0
- package/dist/web/assets/apps-panel-DXAFsveZ.js.gz +0 -0
- package/dist/web/assets/{audio-preview-vaG2cB-k.js → audio-preview-BfES4Cx5.js} +1 -1
- package/dist/web/assets/audio-preview-BfES4Cx5.js.br +0 -0
- package/dist/web/assets/audio-preview-BfES4Cx5.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-CA8hPSX9.js +1 -0
- package/dist/web/assets/branch-review-tab-CA8hPSX9.js.br +0 -0
- package/dist/web/assets/branch-review-tab-CA8hPSX9.js.gz +0 -0
- package/dist/web/assets/{button-Ci9EUgqd.js → button-DeAyLH9U.js} +1 -1
- package/dist/web/assets/button-DeAyLH9U.js.br +0 -0
- package/dist/web/assets/button-DeAyLH9U.js.gz +0 -0
- package/dist/web/assets/{chat-preparation-cache-BSIQGiWx.js → chat-preparation-cache-CcCd2g7A.js} +1 -1
- package/dist/web/assets/chat-preparation-cache-CcCd2g7A.js.br +0 -0
- package/dist/web/assets/chat-preparation-cache-CcCd2g7A.js.gz +0 -0
- package/dist/web/assets/chat-tab-BWi_bUzA.js +1 -0
- package/dist/web/assets/chat-tab-BWi_bUzA.js.br +0 -0
- package/dist/web/assets/chat-tab-BWi_bUzA.js.gz +0 -0
- package/dist/web/assets/chat-tab-BvJhIaVE.js +15 -0
- package/dist/web/assets/chat-tab-BvJhIaVE.js.br +0 -0
- package/dist/web/assets/chat-tab-BvJhIaVE.js.gz +0 -0
- package/dist/web/assets/code-editor-OJUdLVnn.js +7 -0
- package/dist/web/assets/code-editor-OJUdLVnn.js.br +0 -0
- package/dist/web/assets/code-editor-OJUdLVnn.js.gz +0 -0
- package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js +1 -0
- package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-Dgxy_VNp.js.gz +0 -0
- package/dist/web/assets/{conflict-editor-qMGJH0Bx.js → conflict-editor-BpKW_vbL.js} +1 -1
- package/dist/web/assets/conflict-editor-BpKW_vbL.js.br +0 -0
- package/dist/web/assets/conflict-editor-BpKW_vbL.js.gz +0 -0
- package/dist/web/assets/context-menu-7OX80HDB.js +1 -0
- package/dist/web/assets/context-menu-7OX80HDB.js.br +0 -0
- package/dist/web/assets/context-menu-7OX80HDB.js.gz +0 -0
- package/dist/web/assets/{csv-preview-D2I742dl.js → csv-preview-COYdTABa.js} +1 -1
- package/dist/web/assets/csv-preview-COYdTABa.js.br +0 -0
- package/dist/web/assets/csv-preview-COYdTABa.js.gz +0 -0
- package/dist/web/assets/{data-grid-overlay-editor-CQRVKr7y.js → data-grid-overlay-editor-CeZoduke.js} +1 -1
- package/dist/web/assets/data-grid-overlay-editor-CeZoduke.js.br +0 -0
- package/dist/web/assets/data-grid-overlay-editor-CeZoduke.js.gz +0 -0
- package/dist/web/assets/{database-viewer-Bz4LjgIa.js → database-viewer-BQcBqCKm.js} +1 -1
- package/dist/web/assets/database-viewer-BQcBqCKm.js.br +0 -0
- package/dist/web/assets/database-viewer-BQcBqCKm.js.gz +0 -0
- package/dist/web/assets/{design-export-client-ChMcMoqW.js → design-export-client-OrnGCSwu.js} +1 -1
- package/dist/web/assets/design-export-client-OrnGCSwu.js.br +0 -0
- package/dist/web/assets/design-export-client-OrnGCSwu.js.gz +0 -0
- package/dist/web/assets/{design-responsive-dialog-DNBhJJLf.js → design-responsive-dialog-vJjQrC4n.js} +1 -1
- package/dist/web/assets/design-responsive-dialog-vJjQrC4n.js.br +0 -0
- package/dist/web/assets/design-responsive-dialog-vJjQrC4n.js.gz +0 -0
- package/dist/web/assets/design-tab-BbmFk38H.js +33 -0
- package/dist/web/assets/design-tab-BbmFk38H.js.br +0 -0
- package/dist/web/assets/design-tab-BbmFk38H.js.gz +0 -0
- package/dist/web/assets/{designs-sidebar-panel-CapP1yXq.js → designs-sidebar-panel-D2kXwB8B.js} +1 -1
- package/dist/web/assets/designs-sidebar-panel-D2kXwB8B.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-D2kXwB8B.js.gz +0 -0
- package/dist/web/assets/dialog-Bocc0Fgb.js +1 -0
- package/dist/web/assets/dialog-Bocc0Fgb.js.br +0 -0
- package/dist/web/assets/dialog-Bocc0Fgb.js.gz +0 -0
- package/dist/web/assets/{diff-viewer-CdXi7cZN.js → diff-viewer-Bj4DT6Mj.js} +1 -1
- package/dist/web/assets/diff-viewer-Bj4DT6Mj.js.br +0 -0
- package/dist/web/assets/diff-viewer-Bj4DT6Mj.js.gz +0 -0
- package/dist/web/assets/{docx-preview-C9XdKEj1.js → docx-preview-C2QqNGRE.js} +1 -1
- package/dist/web/assets/docx-preview-C2QqNGRE.js.br +0 -0
- package/dist/web/assets/docx-preview-C2QqNGRE.js.gz +0 -0
- package/dist/web/assets/editor-language-service-COqB2tuv.js +1 -0
- package/dist/web/assets/editor-language-service-COqB2tuv.js.br +3 -0
- package/dist/web/assets/editor-language-service-COqB2tuv.js.gz +0 -0
- package/dist/web/assets/explorer-body-DGrYajDN.js +2 -0
- package/dist/web/assets/explorer-body-DGrYajDN.js.br +0 -0
- package/dist/web/assets/explorer-body-DGrYajDN.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-DLzkODTP.js +1 -0
- package/dist/web/assets/explorer-window-content-DLzkODTP.js.br +0 -0
- package/dist/web/assets/explorer-window-content-DLzkODTP.js.gz +0 -0
- package/dist/web/assets/{extension-manager-section-B2nZzJ2b.js → extension-manager-section-Bu0qcB2i.js} +1 -1
- package/dist/web/assets/extension-manager-section-Bu0qcB2i.js.br +0 -0
- package/dist/web/assets/extension-manager-section-Bu0qcB2i.js.gz +0 -0
- package/dist/web/assets/{extension-webview-BGoDxkdF.js → extension-webview-G_WJ0N52.js} +1 -1
- package/dist/web/assets/extension-webview-G_WJ0N52.js.br +0 -0
- package/dist/web/assets/extension-webview-G_WJ0N52.js.gz +0 -0
- package/dist/web/assets/{file-icons-gx_FBOXY.js → file-icons-l7fNOmdS.js} +1 -1
- package/dist/web/assets/file-icons-l7fNOmdS.js.br +0 -0
- package/dist/web/assets/file-icons-l7fNOmdS.js.gz +0 -0
- package/dist/web/assets/{file-store-D9KE0kQC.js → file-store-B0e7sLag.js} +1 -1
- package/dist/web/assets/file-store-B0e7sLag.js.br +0 -0
- package/dist/web/assets/file-store-B0e7sLag.js.gz +0 -0
- package/dist/web/assets/{files-settings-section-DC3OzzOl.js → files-settings-section-ChZ_CynX.js} +1 -1
- package/dist/web/assets/files-settings-section-ChZ_CynX.js.br +0 -0
- package/dist/web/assets/files-settings-section-ChZ_CynX.js.gz +0 -0
- package/dist/web/assets/format-bytes-__396eCN.js +1 -0
- package/dist/web/assets/{format-file-meta-mQwA6SuY.js → format-file-meta-o1XBuoKS.js} +1 -1
- package/dist/web/assets/format-file-meta-o1XBuoKS.js.br +0 -0
- package/dist/web/assets/format-file-meta-o1XBuoKS.js.gz +0 -0
- package/dist/web/assets/{fs-api-DL36slbd.js → fs-api-CeHKwx95.js} +1 -1
- package/dist/web/assets/fs-api-CeHKwx95.js.br +0 -0
- package/dist/web/assets/fs-api-CeHKwx95.js.gz +0 -0
- package/dist/web/assets/{general-settings-section-i41W43ER.js → general-settings-section-MSvDEbFa.js} +1 -1
- package/dist/web/assets/general-settings-section-MSvDEbFa.js.br +0 -0
- package/dist/web/assets/general-settings-section-MSvDEbFa.js.gz +0 -0
- package/dist/web/assets/{git-log-panel-DHzPIkJg.js → git-log-panel-gw5_a6j3.js} +1 -1
- package/dist/web/assets/git-log-panel-gw5_a6j3.js.br +0 -0
- package/dist/web/assets/git-log-panel-gw5_a6j3.js.gz +0 -0
- package/dist/web/assets/{git-repo-store-DSypNj19.js → git-repo-store-BpPnah5l.js} +1 -1
- package/dist/web/assets/git-repo-store-BpPnah5l.js.br +0 -0
- package/dist/web/assets/git-repo-store-BpPnah5l.js.gz +0 -0
- package/dist/web/assets/{glide-data-grid-Dj4vrDvj.js → glide-data-grid-Db2Cl26B.js} +5 -5
- package/dist/web/assets/glide-data-grid-Db2Cl26B.js.br +0 -0
- package/dist/web/assets/glide-data-grid-Db2Cl26B.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-B4etmYTR.js +1 -0
- package/dist/web/assets/group-chat-tab-B4etmYTR.js.br +0 -0
- package/dist/web/assets/group-chat-tab-B4etmYTR.js.gz +0 -0
- package/dist/web/assets/{image-preview-CGdZrmLo.js → image-preview-CjNhpfWw.js} +1 -1
- package/dist/web/assets/index-CtAvmybi.js +52 -0
- package/dist/web/assets/index-CtAvmybi.js.br +0 -0
- package/dist/web/assets/index-CtAvmybi.js.gz +0 -0
- package/dist/web/assets/index-DeCJasyy.css +1 -0
- package/dist/web/assets/index-DeCJasyy.css.br +0 -0
- package/dist/web/assets/index-DeCJasyy.css.gz +0 -0
- package/dist/web/assets/{jira-watcher-section-BJ9edGEP.js → jira-watcher-section-CKSI1Alo.js} +1 -1
- package/dist/web/assets/keybindings-store-C4_yyU4u.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-CxdGSNGr.js → keyboard-shortcuts-section-CEaVUc9a.js} +1 -1
- package/dist/web/assets/keyboard-shortcuts-section-CEaVUc9a.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-CEaVUc9a.js.gz +0 -0
- package/dist/web/assets/language-servers-section-ePtEpnDc.js +2 -0
- package/dist/web/assets/language-servers-section-ePtEpnDc.js.br +0 -0
- package/dist/web/assets/language-servers-section-ePtEpnDc.js.gz +0 -0
- package/dist/web/assets/lsp-client-XzmR3Brr.js +1 -0
- package/dist/web/assets/lsp-client-XzmR3Brr.js.br +0 -0
- package/dist/web/assets/lsp-client-XzmR3Brr.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-CTtDap-l.js +1 -0
- package/dist/web/assets/{markdown-renderer-ok96IgnS.js → markdown-renderer-D05fAr57.js} +2 -2
- package/dist/web/assets/markdown-renderer-D05fAr57.js.br +0 -0
- package/dist/web/assets/markdown-renderer-D05fAr57.js.gz +0 -0
- package/dist/web/assets/{media-url-CzUJrREv.js → media-url-C6NumuN5.js} +1 -1
- package/dist/web/assets/metric-chart-canvas-DHJe0VER.js +1 -0
- package/dist/web/assets/metric-chart-canvas-DHJe0VER.js.br +0 -0
- package/dist/web/assets/metric-chart-canvas-DHJe0VER.js.gz +0 -0
- package/dist/web/assets/{mobile-bottom-sheet-CuNi2FRv.js → mobile-bottom-sheet-D4UyOatB.js} +1 -1
- package/dist/web/assets/mobile-bottom-sheet-D4UyOatB.js.br +0 -0
- package/dist/web/assets/mobile-bottom-sheet-D4UyOatB.js.gz +0 -0
- package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js +1 -0
- package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-CcfGdGL8.js.gz +0 -0
- package/dist/web/assets/{new-design-dialog-Drkjb_LI.js → new-design-dialog-BgmQopJM.js} +1 -1
- package/dist/web/assets/new-design-dialog-BgmQopJM.js.br +0 -0
- package/dist/web/assets/new-design-dialog-BgmQopJM.js.gz +0 -0
- package/dist/web/assets/notification-store-q5QBklKA.js +1 -0
- package/dist/web/assets/{notifications-settings-section-C05WYqcE.js → notifications-settings-section-BBJ0r9OS.js} +1 -1
- package/dist/web/assets/notifications-settings-section-BBJ0r9OS.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-BBJ0r9OS.js.gz +0 -0
- package/dist/web/assets/{onboarding-entry-CmUqv1kl.js → onboarding-entry-BYOYswsi.js} +1 -1
- package/dist/web/assets/onboarding-entry-BYOYswsi.js.br +0 -0
- package/dist/web/assets/onboarding-entry-BYOYswsi.js.gz +0 -0
- package/dist/web/assets/{onboarding-quick-orientation-DIhWv1CN.js → onboarding-quick-orientation-DPHYFMbA.js} +1 -1
- package/dist/web/assets/onboarding-quick-orientation-DPHYFMbA.js.br +0 -0
- package/dist/web/assets/onboarding-quick-orientation-DPHYFMbA.js.gz +0 -0
- package/dist/web/assets/overview-panel-pkPs44Tv.js +1 -0
- package/dist/web/assets/overview-panel-pkPs44Tv.js.br +0 -0
- package/dist/web/assets/overview-panel-pkPs44Tv.js.gz +0 -0
- package/dist/web/assets/{pdf-preview-qdXmMkOR.js → pdf-preview-c-L_kDAs.js} +1 -1
- package/dist/web/assets/pdf-preview-c-L_kDAs.js.br +0 -0
- package/dist/web/assets/pdf-preview-c-L_kDAs.js.gz +0 -0
- package/dist/web/assets/performance-panel-yPrq0DJV.js +3 -0
- package/dist/web/assets/performance-panel-yPrq0DJV.js.br +0 -0
- package/dist/web/assets/performance-panel-yPrq0DJV.js.gz +0 -0
- package/dist/web/assets/{postgres-viewer-Xvlun_n5.js → postgres-viewer-CnwDCMNd.js} +1 -1
- package/dist/web/assets/postgres-viewer-CnwDCMNd.js.br +0 -0
- package/dist/web/assets/postgres-viewer-CnwDCMNd.js.gz +0 -0
- package/dist/web/assets/{ppmbot-settings-section-C8PFlvaq.js → ppmbot-settings-section-BDXoSIDp.js} +1 -1
- package/dist/web/assets/ppmbot-settings-section-BDXoSIDp.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-BDXoSIDp.js.gz +0 -0
- package/dist/web/assets/{pptx-export-CG6MRNok.js → pptx-export-D0RDY48h.js} +4 -4
- package/dist/web/assets/pptx-export-D0RDY48h.js.br +0 -0
- package/dist/web/assets/pptx-export-D0RDY48h.js.gz +0 -0
- package/dist/web/assets/{problems-panel-CFYO0Ro3.js → problems-panel-BgY8oyo9.js} +1 -1
- package/dist/web/assets/problems-panel-BgY8oyo9.js.br +0 -0
- package/dist/web/assets/problems-panel-BgY8oyo9.js.gz +0 -0
- package/dist/web/assets/process-row-format-CaaMcHzn.js +1 -0
- package/dist/web/assets/process-table-BOwDtXuG.js +1 -0
- package/dist/web/assets/process-table-BOwDtXuG.js.br +0 -0
- package/dist/web/assets/process-table-BOwDtXuG.js.gz +0 -0
- package/dist/web/assets/{project-framework-store-XxFXcUpb.js → project-framework-store-UDEMu28U.js} +1 -1
- package/dist/web/assets/{project-store-C5gJXkvb.js → project-store-DGa31y5B.js} +1 -1
- package/dist/web/assets/project-store-DGa31y5B.js.br +0 -0
- package/dist/web/assets/project-store-DGa31y5B.js.gz +0 -0
- package/dist/web/assets/{proxy-settings-section-BcHIEANE.js → proxy-settings-section-Db6Kc6xG.js} +1 -1
- package/dist/web/assets/proxy-settings-section-Db6Kc6xG.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-Db6Kc6xG.js.gz +0 -0
- package/dist/web/assets/{query-audit-section-UKG_smyt.js → query-audit-section-C0PAXhfT.js} +1 -1
- package/dist/web/assets/query-audit-section-C0PAXhfT.js.br +0 -0
- package/dist/web/assets/query-audit-section-C0PAXhfT.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-clipboard-notice-BGsp0edA.js → remote-desktop-clipboard-notice-DOVtobj_.js} +1 -1
- package/dist/web/assets/remote-desktop-clipboard-notice-DOVtobj_.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-DOVtobj_.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DXlU3msM.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js +1 -0
- package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-ClV4YTLU.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-readiness-gate-CsZCCRJI.js → remote-desktop-readiness-gate-BsfoJzdc.js} +1 -1
- package/dist/web/assets/remote-desktop-readiness-gate-BsfoJzdc.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-BsfoJzdc.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-kDaXl1_Q.js.gz +0 -0
- package/dist/web/assets/{schedules-settings-section-Ci0O2Ng6.js → schedules-settings-section-CvbjQM8z.js} +1 -1
- package/dist/web/assets/schedules-settings-section-CvbjQM8z.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-CvbjQM8z.js.gz +0 -0
- package/dist/web/assets/{scroll-area-XonvWYn_.js → scroll-area-sjOeAfoD.js} +1 -1
- package/dist/web/assets/scroll-area-sjOeAfoD.js.br +0 -0
- package/dist/web/assets/scroll-area-sjOeAfoD.js.gz +0 -0
- package/dist/web/assets/{separator-sQj04Ari.js → separator-9A3PCG1t.js} +1 -1
- package/dist/web/assets/services-panel-CimW47P8.js +1 -0
- package/dist/web/assets/services-panel-CimW47P8.js.br +0 -0
- package/dist/web/assets/services-panel-CimW47P8.js.gz +0 -0
- package/dist/web/assets/settings-body-BTXNtpWL.js +2 -0
- package/dist/web/assets/settings-body-BTXNtpWL.js.br +0 -0
- package/dist/web/assets/settings-body-BTXNtpWL.js.gz +0 -0
- package/dist/web/assets/settings-store-DvftLqzy.js +2 -0
- package/dist/web/assets/settings-store-DvftLqzy.js.br +0 -0
- package/dist/web/assets/settings-store-DvftLqzy.js.gz +0 -0
- package/dist/web/assets/settings-tab-CGhaRXjs.js +1 -0
- package/dist/web/assets/settings-window-content--Uyg_Psr.js +1 -0
- package/dist/web/assets/sortable-header-DUW9T1Hz.js +1 -0
- package/dist/web/assets/sortable-header-DUW9T1Hz.js.br +0 -0
- package/dist/web/assets/sortable-header-DUW9T1Hz.js.gz +0 -0
- package/dist/web/assets/{sql-query-editor-4iCGE4h2.js → sql-query-editor-Dn-cgDrW.js} +1 -1
- package/dist/web/assets/sql-query-editor-Dn-cgDrW.js.br +0 -0
- package/dist/web/assets/sql-query-editor-Dn-cgDrW.js.gz +0 -0
- package/dist/web/assets/{sqlite-viewer-CK6DulYk.js → sqlite-viewer-B0NfaMjq.js} +1 -1
- package/dist/web/assets/sqlite-viewer-B0NfaMjq.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-B0NfaMjq.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-CxCfRQFb.js +2 -0
- package/dist/web/assets/system-monitor-body-CxCfRQFb.js.br +0 -0
- package/dist/web/assets/system-monitor-body-CxCfRQFb.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-Cb3EZUdp.js +1 -0
- package/dist/web/assets/system-monitor-window-content-51CA-2PH.js +1 -0
- package/dist/web/assets/{tab-host-window-content-CWBaWKjA.js → tab-host-window-content-CSrcmKNG.js} +1 -1
- package/dist/web/assets/tab-host-window-content-CSrcmKNG.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-CSrcmKNG.js.gz +0 -0
- package/dist/web/assets/{team-member-sheet-Br6mrxrw.js → team-member-sheet-D4X6nh-x.js} +2 -2
- package/dist/web/assets/team-member-sheet-D4X6nh-x.js.br +0 -0
- package/dist/web/assets/team-member-sheet-D4X6nh-x.js.gz +0 -0
- package/dist/web/assets/{team-member-window-content-CwCy7ZWf.js → team-member-window-content-BxkbGVIL.js} +1 -1
- package/dist/web/assets/team-member-window-content-BxkbGVIL.js.br +0 -0
- package/dist/web/assets/team-member-window-content-BxkbGVIL.js.gz +0 -0
- package/dist/web/assets/{terminal-tab-BW14RpCb.js → terminal-tab-BYo34IqU.js} +3 -3
- package/dist/web/assets/terminal-tab-BYo34IqU.js.br +0 -0
- package/dist/web/assets/terminal-tab-BYo34IqU.js.gz +0 -0
- package/dist/web/assets/{tool-cards-CgOBEUnb.js → tool-cards-DvXjOped.js} +4 -4
- package/dist/web/assets/tool-cards-DvXjOped.js.br +0 -0
- package/dist/web/assets/tool-cards-DvXjOped.js.gz +0 -0
- package/dist/web/assets/{use-blob-url-CmavyEP8.js → use-blob-url-CISBWKG5.js} +1 -1
- package/dist/web/assets/use-inline-blame-Br6mheS4.js +16 -0
- package/dist/web/assets/use-inline-blame-Br6mheS4.js.br +0 -0
- package/dist/web/assets/use-inline-blame-Br6mheS4.js.gz +0 -0
- package/dist/web/assets/use-lsp-BpJmvQ0n.js +6 -0
- package/dist/web/assets/use-lsp-BpJmvQ0n.js.br +0 -0
- package/dist/web/assets/use-lsp-BpJmvQ0n.js.gz +0 -0
- package/dist/web/assets/{use-monaco-theme-rH6epJYA.js → use-monaco-theme-UGOzyIhv.js} +1 -1
- package/dist/web/assets/use-process-kill-CyiiUD1E.js +1 -0
- package/dist/web/assets/use-process-kill-CyiiUD1E.js.br +0 -0
- package/dist/web/assets/use-process-kill-CyiiUD1E.js.gz +0 -0
- package/dist/web/assets/{use-websocket-QwURkjpV.js → use-websocket-Dpl21cHG.js} +1 -1
- package/dist/web/assets/{vendor-ui-BHh12_0v.js → vendor-ui-P9Ym7hCv.js} +1 -1
- package/dist/web/assets/vendor-ui-P9Ym7hCv.js.br +0 -0
- package/dist/web/assets/vendor-ui-P9Ym7hCv.js.gz +0 -0
- package/dist/web/assets/video-preview-D6Jw-IZK.js +1 -0
- package/dist/web/assets/video-preview-D6Jw-IZK.js.br +0 -0
- package/dist/web/assets/video-preview-D6Jw-IZK.js.gz +0 -0
- package/dist/web/assets/voice-settings-section-CcHQDYzN.js +1 -0
- package/dist/web/assets/voice-settings-section-CcHQDYzN.js.br +0 -0
- package/dist/web/assets/voice-settings-section-CcHQDYzN.js.gz +0 -0
- package/dist/web/assets/ws-auth-CqI8-aoA.js +1 -0
- package/dist/web/index.html +20 -20
- package/dist/web/index.html.br +0 -0
- package/dist/web/index.html.gz +0 -0
- package/dist/web/sw.js +1 -1
- package/dist/web/sw.js.br +0 -0
- package/dist/web/sw.js.gz +0 -0
- package/docs/deployment-guide.md +23 -0
- package/package.json +1 -1
- package/release-from-worktree.sh +224 -0
- package/src/cli/commands/config-cmd.ts +12 -0
- package/src/providers/claude-agent-sdk.ts +65 -2
- package/src/providers/codex-app-server/codex-jsonrpc-client.ts +16 -1
- package/src/providers/codex-app-server/codex-provider.ts +39 -7
- package/src/providers/codex-app-server/codex-reset-credit.ts +49 -0
- package/src/providers/provider-probe.ts +63 -0
- package/src/providers/registry.ts +109 -29
- package/src/server/index.ts +9 -0
- package/src/server/middleware/auth.ts +5 -1
- package/src/server/routes/codex-accounts.ts +14 -0
- package/src/server/routes/cross-origin-guard.ts +20 -0
- package/src/server/routes/git.ts +40 -4
- package/src/server/routes/lsp.ts +97 -4
- package/src/server/routes/resources.ts +72 -9
- package/src/server/routes/settings.ts +19 -1
- package/src/server/routes/speech.ts +66 -0
- package/src/server/routes/system-services.ts +76 -0
- package/src/server/routes/tunnel.ts +33 -1
- package/src/server/ws/chat.ts +74 -3
- package/src/services/codex-account-login.ts +26 -2
- package/src/services/codex-reset-credit.service.ts +83 -0
- package/src/services/compaction-savings.ts +127 -0
- package/src/services/db.service.ts +123 -8
- package/src/services/edge-forwarder.ts +191 -191
- package/src/services/fs-ops/fs-ops-trash.service.ts +22 -9
- package/src/services/git-refs/for-each-ref.ts +118 -0
- package/src/services/git.service.ts +25 -2
- package/src/services/host-info/spawn-runner.ts +48 -4
- package/src/services/jsonl-transcript-parser.ts +7 -42
- package/src/services/lsp/lsp-install.ts +289 -0
- package/src/services/lsp/lsp-manager.ts +160 -29
- package/src/services/lsp/server-registry.ts +120 -4
- package/src/services/named-tunnel/named-tunnel-config.ts +8 -2
- package/src/services/named-tunnel/named-tunnel-setup.service.ts +4 -0
- package/src/services/read-lines.ts +54 -0
- package/src/services/speech-to-text/whisper-catalog.ts +142 -0
- package/src/services/speech-to-text/whisper-download.ts +113 -0
- package/src/services/speech-to-text/whisper-hallucinations.ts +51 -0
- package/src/services/speech-to-text/whisper-install.service.ts +247 -0
- package/src/services/speech-to-text/whisper-paths.ts +62 -0
- package/src/services/speech-to-text/whisper-transcribe.service.ts +117 -0
- package/src/services/supervisor.ts +94 -11
- package/src/services/system-metrics/cpu-details-linux.ts +252 -0
- package/src/services/system-metrics/cpu-memory-collector.ts +140 -18
- package/src/services/system-metrics/device-collector-linux.ts +31 -0
- package/src/services/system-metrics/device-collector-types.ts +40 -0
- package/src/services/system-metrics/disk-devices-linux.ts +156 -0
- package/src/services/system-metrics/disk-inventory-linux.ts +150 -0
- package/src/services/system-metrics/fans-linux.ts +51 -0
- package/src/services/system-metrics/gpu-api-versions.ts +120 -0
- package/src/services/system-metrics/gpu-devices-linux.ts +370 -0
- package/src/services/system-metrics/gpu-fdinfo-linux.ts +253 -0
- package/src/services/system-metrics/hardware-inventory.ts +50 -0
- package/src/services/system-metrics/linux-fs.ts +43 -0
- package/src/services/system-metrics/memory-darwin.ts +91 -0
- package/src/services/system-metrics/memory-linux.ts +156 -0
- package/src/services/system-metrics/net-devices-linux.ts +114 -0
- package/src/services/system-metrics/net-inventory-linux.ts +100 -0
- package/src/services/system-metrics/partitions-linux.ts +158 -0
- package/src/services/system-metrics/process-collector-linux.ts +58 -6
- package/src/services/system-metrics/process-collector-types.ts +8 -1
- package/src/services/system-metrics/process-details-darwin.ts +166 -0
- package/src/services/system-metrics/process-details-linux.ts +150 -0
- package/src/services/system-metrics/process-grouping.ts +2 -1
- package/src/services/system-metrics/process-rows-builder.ts +2 -0
- package/src/services/system-metrics/process-swap-linux.ts +37 -0
- package/src/services/system-metrics/signal-executor.ts +84 -0
- package/src/services/system-metrics/signal-request-handler.ts +92 -0
- package/src/services/system-metrics/system-metrics-platform.ts +30 -4
- package/src/services/system-metrics/system-metrics-tick.ts +73 -4
- package/src/services/system-metrics/system-metrics.service.ts +87 -2
- package/src/services/system-metrics/udev-db.ts +40 -0
- package/src/services/system-services/app-cgroup-linux.ts +115 -0
- package/src/services/system-services/app-icon-service.ts +31 -0
- package/src/services/system-services/app-icons-linux.ts +124 -0
- package/src/services/system-services/apps-linux.ts +174 -0
- package/src/services/system-services/desktop-entry.ts +103 -0
- package/src/services/system-services/service-guard.ts +124 -0
- package/src/services/system-services/systemd-collector.ts +203 -0
- package/src/services/system-services/systemd-parse.ts +162 -0
- package/src/services/windows-process-tree.ts +351 -351
- package/src/shared/prompt-cache-idle.ts +197 -0
- package/src/shared/turn-usage.ts +85 -1
- package/src/shared/usage-extra.ts +19 -0
- package/src/types/chat.ts +29 -0
- package/src/types/config.ts +14 -0
- package/src/types/git.ts +41 -0
- package/src/types/system-hardware.ts +137 -0
- package/src/types/system-metrics.ts +231 -2
- package/src/types/system-services.ts +73 -0
- package/src/web/components/branch-review/branch-review-tab.tsx +20 -20
- package/src/web/components/branch-review/branch-review-tree-row.tsx +1 -13
- package/src/web/components/chat/chat-tab.tsx +3 -0
- package/src/web/components/chat/codex-usage-panel.tsx +4 -0
- package/src/web/components/chat/compaction-divider.tsx +48 -0
- package/src/web/components/chat/idle-cache-notice.tsx +67 -0
- package/src/web/components/chat/message-input.tsx +48 -13
- package/src/web/components/chat/message-list.tsx +15 -0
- package/src/web/components/chat/prompt-cache-chip.tsx +74 -0
- package/src/web/components/editor/code-editor.tsx +1 -0
- package/src/web/components/editor/editor-language-service.tsx +11 -3
- package/src/web/components/editor/lsp-status.tsx +115 -13
- package/src/web/components/git/branch-picker.tsx +419 -0
- package/src/web/components/git/branch-select.tsx +34 -0
- package/src/web/components/git/create-worktree-dialog.tsx +17 -16
- package/src/web/components/git/git-repo-picker.tsx +28 -40
- package/src/web/components/layout/status-bar.tsx +42 -19
- package/src/web/components/settings/accounts/account-card.tsx +10 -2
- package/src/web/components/settings/accounts/account-usage-extras.tsx +3 -2
- package/src/web/components/settings/accounts/codex-accounts-section.tsx +3 -1
- package/src/web/components/settings/accounts/codex-reset-credit-button.tsx +120 -0
- package/src/web/components/settings/ai-settings-section.tsx +67 -3
- package/src/web/components/settings/appearance-settings-section.tsx +2 -28
- package/src/web/components/settings/language-servers-section.tsx +362 -0
- package/src/web/components/settings/settings-categories.ts +8 -3
- package/src/web/components/settings/settings-section-content.tsx +2 -0
- package/src/web/components/settings/voice-settings-section.tsx +283 -0
- package/src/web/components/system/apps/app-row.tsx +110 -0
- package/src/web/components/system/apps/app-rows.ts +140 -0
- package/src/web/components/system/apps/apps-panel.tsx +129 -0
- package/src/web/components/system/metric-chart-canvas.tsx +79 -3
- package/src/web/components/system/overview-cards/card-shell.tsx +54 -0
- package/src/web/components/system/overview-cards/cpu-card.tsx +11 -6
- package/src/web/components/system/overview-cards/disk-card.tsx +11 -6
- package/src/web/components/system/overview-cards/gpu-card.tsx +16 -6
- package/src/web/components/system/overview-cards/mem-card.tsx +11 -6
- package/src/web/components/system/overview-cards/net-card.tsx +11 -6
- package/src/web/components/system/overview-device-target.ts +69 -0
- package/src/web/components/system/overview-panel.tsx +15 -1
- package/src/web/components/system/performance/cpu-core-grid.tsx +74 -0
- package/src/web/components/system/performance/cpu-detail.tsx +220 -0
- package/src/web/components/system/performance/detail-parts.tsx +149 -0
- package/src/web/components/system/performance/device-list.ts +139 -0
- package/src/web/components/system/performance/disk-detail.tsx +64 -0
- package/src/web/components/system/performance/fans-detail.tsx +35 -0
- package/src/web/components/system/performance/gpu-detail.tsx +116 -0
- package/src/web/components/system/performance/memory-detail.tsx +109 -0
- package/src/web/components/system/performance/nic-detail.tsx +55 -0
- package/src/web/components/system/performance/partition-list.tsx +79 -0
- package/src/web/components/system/performance/performance-panel.tsx +206 -0
- package/src/web/components/system/performance/use-hardware-inventory.ts +48 -0
- package/src/web/components/system/process-columns-grid.ts +5 -3
- package/src/web/components/system/process-details-dialog.tsx +116 -0
- package/src/web/components/system/process-group-row.tsx +6 -1
- package/src/web/components/system/process-row-format.ts +6 -0
- package/src/web/components/system/process-row-menu.tsx +72 -0
- package/src/web/components/system/process-row.tsx +7 -1
- package/src/web/components/system/process-table-model.ts +1 -1
- package/src/web/components/system/process-table-sort.ts +8 -4
- package/src/web/components/system/process-table-toolbar.tsx +10 -2
- package/src/web/components/system/process-table-totals.ts +3 -0
- package/src/web/components/system/process-table.tsx +25 -2
- package/src/web/components/system/services/service-action-confirm.tsx +100 -0
- package/src/web/components/system/services/service-columns.ts +103 -0
- package/src/web/components/system/services/service-details-sheet.tsx +97 -0
- package/src/web/components/system/services/service-resources.ts +105 -0
- package/src/web/components/system/services/service-row.tsx +146 -0
- package/src/web/components/system/services/service-rows.ts +92 -0
- package/src/web/components/system/services/service-sort.ts +90 -0
- package/src/web/components/system/services/services-header.tsx +51 -0
- package/src/web/components/system/services/services-panel.tsx +207 -0
- package/src/web/components/system/services/use-services.ts +92 -0
- package/src/web/components/system/sortable-header.tsx +13 -8
- package/src/web/components/system/sysmon-preferences.tsx +85 -0
- package/src/web/components/system/system-monitor-body.tsx +101 -43
- package/src/web/components/system/system-monitor-window-content.tsx +2 -3
- package/src/web/components/system/use-process-signal.ts +87 -0
- package/src/web/components/tunnels/public-tunnel-switch.tsx +82 -0
- package/src/web/components/tunnels/tunnel-manager-tab.tsx +4 -0
- package/src/web/components/ui/dialog.tsx +14 -1
- package/src/web/components/ui/search-select.tsx +279 -0
- package/src/web/components/ui/start-ellipsis.tsx +18 -0
- package/src/web/hooks/use-chat.ts +28 -0
- package/src/web/hooks/use-global-keybindings.ts +7 -5
- package/src/web/hooks/use-lsp.ts +23 -2
- package/src/web/hooks/use-minute-clock.ts +20 -0
- package/src/web/hooks/use-whisper-voice-input.ts +113 -0
- package/src/web/lib/api-tunnels.ts +19 -0
- package/src/web/lib/branch-select-items.ts +29 -0
- package/src/web/lib/cpu-graph-mode.ts +90 -0
- package/src/web/lib/format-bytes.ts +14 -0
- package/src/web/lib/git-ref-picker.ts +120 -0
- package/src/web/lib/lsp/lsp-client.ts +32 -0
- package/src/web/lib/search-select-rows.ts +55 -0
- package/src/web/lib/temperature.ts +27 -0
- package/src/web/lib/voice-wav.ts +69 -0
- package/src/web/stores/git-status-store.ts +31 -17
- package/src/web/stores/keybindings-store.ts +14 -1
- package/src/web/stores/settings-store.ts +65 -0
- package/src/web/styles/globals.css +11 -0
- package/dist/web/assets/accounts-settings-section-CgG5P3Jm.js +0 -1
- package/dist/web/assets/accounts-settings-section-CgG5P3Jm.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-CgG5P3Jm.js.gz +0 -0
- package/dist/web/assets/ai-resource-editor-DKcsVDle.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-DKcsVDle.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-D7zJ-hCO.js +0 -1
- package/dist/web/assets/ai-settings-section-D7zJ-hCO.js.br +0 -0
- package/dist/web/assets/ai-settings-section-D7zJ-hCO.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-OPqC2bYG.js +0 -1
- package/dist/web/assets/api-client-BgedDTvo.js +0 -1
- package/dist/web/assets/api-client-c8YOpTlT.js.br +0 -0
- package/dist/web/assets/api-client-c8YOpTlT.js.gz +0 -0
- package/dist/web/assets/api-settings-BmvNoCbn.js.br +0 -0
- package/dist/web/assets/api-settings-BmvNoCbn.js.gz +0 -0
- package/dist/web/assets/api-settings-Bti1-Zn3.js +0 -1
- package/dist/web/assets/appearance-settings-section-DL466x83.js +0 -1
- package/dist/web/assets/appearance-settings-section-DL466x83.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-DL466x83.js.gz +0 -0
- package/dist/web/assets/audio-preview-vaG2cB-k.js.br +0 -0
- package/dist/web/assets/audio-preview-vaG2cB-k.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-Dag86n_R.js +0 -1
- package/dist/web/assets/branch-review-tab-Dag86n_R.js.br +0 -0
- package/dist/web/assets/branch-review-tab-Dag86n_R.js.gz +0 -0
- package/dist/web/assets/button-Ci9EUgqd.js.br +0 -0
- package/dist/web/assets/button-Ci9EUgqd.js.gz +0 -0
- package/dist/web/assets/chat-preparation-cache-BSIQGiWx.js.br +0 -0
- package/dist/web/assets/chat-preparation-cache-BSIQGiWx.js.gz +0 -0
- package/dist/web/assets/chat-tab-CcYZLXls.js +0 -15
- package/dist/web/assets/chat-tab-CcYZLXls.js.br +0 -0
- package/dist/web/assets/chat-tab-CcYZLXls.js.gz +0 -0
- package/dist/web/assets/chat-tab-QFDhzzBZ.js +0 -1
- package/dist/web/assets/chat-tab-QFDhzzBZ.js.br +0 -0
- package/dist/web/assets/chat-tab-QFDhzzBZ.js.gz +0 -0
- package/dist/web/assets/code-editor-K-IN8VwJ.js +0 -7
- package/dist/web/assets/code-editor-K-IN8VwJ.js.br +0 -0
- package/dist/web/assets/code-editor-K-IN8VwJ.js.gz +0 -0
- package/dist/web/assets/codex-sign-in-again-dialog-C8JvblLG.js +0 -1
- package/dist/web/assets/codex-sign-in-again-dialog-C8JvblLG.js.br +0 -0
- package/dist/web/assets/codex-sign-in-again-dialog-C8JvblLG.js.gz +0 -0
- package/dist/web/assets/conflict-editor-qMGJH0Bx.js.br +0 -0
- package/dist/web/assets/conflict-editor-qMGJH0Bx.js.gz +0 -0
- package/dist/web/assets/context-menu-CjYTq8DI.js +0 -1
- package/dist/web/assets/context-menu-CjYTq8DI.js.br +0 -0
- package/dist/web/assets/context-menu-CjYTq8DI.js.gz +0 -0
- package/dist/web/assets/csv-preview-D2I742dl.js.br +0 -0
- package/dist/web/assets/csv-preview-D2I742dl.js.gz +0 -0
- package/dist/web/assets/data-grid-overlay-editor-CQRVKr7y.js.br +0 -0
- package/dist/web/assets/data-grid-overlay-editor-CQRVKr7y.js.gz +0 -0
- package/dist/web/assets/database-viewer-Bz4LjgIa.js.br +0 -0
- package/dist/web/assets/database-viewer-Bz4LjgIa.js.gz +0 -0
- package/dist/web/assets/design-export-client-ChMcMoqW.js.br +0 -0
- package/dist/web/assets/design-export-client-ChMcMoqW.js.gz +0 -0
- package/dist/web/assets/design-responsive-dialog-DNBhJJLf.js.br +0 -0
- package/dist/web/assets/design-responsive-dialog-DNBhJJLf.js.gz +0 -0
- package/dist/web/assets/design-tab-C987MtMR.js +0 -33
- package/dist/web/assets/design-tab-C987MtMR.js.br +0 -0
- package/dist/web/assets/design-tab-C987MtMR.js.gz +0 -0
- package/dist/web/assets/designs-sidebar-panel-CapP1yXq.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-CapP1yXq.js.gz +0 -0
- package/dist/web/assets/dialog-B8clVkyi.js +0 -1
- package/dist/web/assets/dialog-B8clVkyi.js.br +0 -0
- package/dist/web/assets/dialog-B8clVkyi.js.gz +0 -0
- package/dist/web/assets/diff-viewer-CdXi7cZN.js.br +0 -0
- package/dist/web/assets/diff-viewer-CdXi7cZN.js.gz +0 -0
- package/dist/web/assets/docx-preview-C9XdKEj1.js.br +0 -0
- package/dist/web/assets/docx-preview-C9XdKEj1.js.gz +0 -0
- package/dist/web/assets/editor-language-service-Zle_PZV8.js +0 -1
- package/dist/web/assets/editor-language-service-Zle_PZV8.js.br +0 -0
- package/dist/web/assets/editor-language-service-Zle_PZV8.js.gz +0 -0
- package/dist/web/assets/explorer-body-DHMQR1Rk.js +0 -2
- package/dist/web/assets/explorer-body-DHMQR1Rk.js.br +0 -0
- package/dist/web/assets/explorer-body-DHMQR1Rk.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-Df40nQBk.js +0 -1
- package/dist/web/assets/explorer-window-content-Df40nQBk.js.br +0 -0
- package/dist/web/assets/explorer-window-content-Df40nQBk.js.gz +0 -0
- package/dist/web/assets/extension-manager-section-B2nZzJ2b.js.br +0 -0
- package/dist/web/assets/extension-manager-section-B2nZzJ2b.js.gz +0 -0
- package/dist/web/assets/extension-webview-BGoDxkdF.js.br +0 -0
- package/dist/web/assets/extension-webview-BGoDxkdF.js.gz +0 -0
- package/dist/web/assets/file-icons-gx_FBOXY.js.br +0 -0
- package/dist/web/assets/file-icons-gx_FBOXY.js.gz +0 -0
- package/dist/web/assets/file-store-D9KE0kQC.js.br +0 -0
- package/dist/web/assets/file-store-D9KE0kQC.js.gz +0 -0
- package/dist/web/assets/files-settings-section-DC3OzzOl.js.br +0 -0
- package/dist/web/assets/files-settings-section-DC3OzzOl.js.gz +0 -0
- package/dist/web/assets/format-bytes-DmSHAKFe.js +0 -1
- package/dist/web/assets/format-file-meta-mQwA6SuY.js.br +0 -0
- package/dist/web/assets/format-file-meta-mQwA6SuY.js.gz +0 -0
- package/dist/web/assets/fs-api-DL36slbd.js.br +0 -0
- package/dist/web/assets/fs-api-DL36slbd.js.gz +0 -0
- package/dist/web/assets/general-settings-section-i41W43ER.js.br +0 -0
- package/dist/web/assets/general-settings-section-i41W43ER.js.gz +0 -0
- package/dist/web/assets/git-log-panel-DHzPIkJg.js.br +0 -0
- package/dist/web/assets/git-log-panel-DHzPIkJg.js.gz +0 -0
- package/dist/web/assets/git-repo-store-DSypNj19.js.br +0 -0
- package/dist/web/assets/git-repo-store-DSypNj19.js.gz +0 -0
- package/dist/web/assets/glide-data-grid-Dj4vrDvj.js.br +0 -0
- package/dist/web/assets/glide-data-grid-Dj4vrDvj.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-DfwZb-fI.js +0 -1
- package/dist/web/assets/group-chat-tab-DfwZb-fI.js.br +0 -0
- package/dist/web/assets/group-chat-tab-DfwZb-fI.js.gz +0 -0
- package/dist/web/assets/index-DMA-So69.css +0 -1
- package/dist/web/assets/index-DMA-So69.css.br +0 -0
- package/dist/web/assets/index-DMA-So69.css.gz +0 -0
- package/dist/web/assets/index-jG9mLs-J.js +0 -52
- package/dist/web/assets/index-jG9mLs-J.js.br +0 -0
- package/dist/web/assets/index-jG9mLs-J.js.gz +0 -0
- package/dist/web/assets/keybindings-store-BlapkC6O.js +0 -1
- package/dist/web/assets/keyboard-shortcuts-section-CxdGSNGr.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-CxdGSNGr.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-DJ9EVU1B.js +0 -1
- package/dist/web/assets/markdown-renderer-ok96IgnS.js.br +0 -0
- package/dist/web/assets/markdown-renderer-ok96IgnS.js.gz +0 -0
- package/dist/web/assets/mobile-bottom-sheet-CuNi2FRv.js.br +0 -0
- package/dist/web/assets/mobile-bottom-sheet-CuNi2FRv.js.gz +0 -0
- package/dist/web/assets/mobile-explorer-sheet-Cc03LUKy.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-Cc03LUKy.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-Cc03LUKy.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-Drkjb_LI.js.br +0 -0
- package/dist/web/assets/new-design-dialog-Drkjb_LI.js.gz +0 -0
- package/dist/web/assets/notification-store-DCyxho0O.js +0 -1
- package/dist/web/assets/notifications-settings-section-C05WYqcE.js.br +0 -0
- package/dist/web/assets/notifications-settings-section-C05WYqcE.js.gz +0 -0
- package/dist/web/assets/onboarding-entry-CmUqv1kl.js.br +0 -0
- package/dist/web/assets/onboarding-entry-CmUqv1kl.js.gz +0 -0
- package/dist/web/assets/onboarding-quick-orientation-DIhWv1CN.js.br +0 -0
- package/dist/web/assets/onboarding-quick-orientation-DIhWv1CN.js.gz +0 -0
- package/dist/web/assets/overview-panel-BZAW_nag.js +0 -1
- package/dist/web/assets/overview-panel-BZAW_nag.js.br +0 -0
- package/dist/web/assets/overview-panel-BZAW_nag.js.gz +0 -0
- package/dist/web/assets/pdf-preview-qdXmMkOR.js.br +0 -0
- package/dist/web/assets/pdf-preview-qdXmMkOR.js.gz +0 -0
- package/dist/web/assets/postgres-viewer-Xvlun_n5.js.br +0 -0
- package/dist/web/assets/postgres-viewer-Xvlun_n5.js.gz +0 -0
- package/dist/web/assets/ppmbot-settings-section-C8PFlvaq.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-C8PFlvaq.js.gz +0 -0
- package/dist/web/assets/pptx-export-CG6MRNok.js.br +0 -0
- package/dist/web/assets/pptx-export-CG6MRNok.js.gz +0 -0
- package/dist/web/assets/problems-panel-CFYO0Ro3.js.br +0 -0
- package/dist/web/assets/problems-panel-CFYO0Ro3.js.gz +0 -0
- package/dist/web/assets/process-table-BVeRguFg.js +0 -1
- package/dist/web/assets/process-table-BVeRguFg.js.br +0 -0
- package/dist/web/assets/process-table-BVeRguFg.js.gz +0 -0
- package/dist/web/assets/project-store-C5gJXkvb.js.br +0 -0
- package/dist/web/assets/project-store-C5gJXkvb.js.gz +0 -0
- package/dist/web/assets/proxy-settings-section-BcHIEANE.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-BcHIEANE.js.gz +0 -0
- package/dist/web/assets/query-audit-section-UKG_smyt.js.br +0 -0
- package/dist/web/assets/query-audit-section-UKG_smyt.js.gz +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-BGsp0edA.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-BGsp0edA.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DxkmF8qb.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-sheet-DxkmF8qb.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DxkmF8qb.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DMD0M9Jh.js +0 -1
- package/dist/web/assets/remote-desktop-mobile-view-DMD0M9Jh.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DMD0M9Jh.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CsZCCRJI.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-CsZCCRJI.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-DrHw_vwY.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-DrHw_vwY.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-DrHw_vwY.js.gz +0 -0
- package/dist/web/assets/schedules-settings-section-Ci0O2Ng6.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-Ci0O2Ng6.js.gz +0 -0
- package/dist/web/assets/scroll-area-XonvWYn_.js.br +0 -0
- package/dist/web/assets/scroll-area-XonvWYn_.js.gz +0 -0
- package/dist/web/assets/settings-body-B6JGOIYb.js +0 -2
- package/dist/web/assets/settings-body-B6JGOIYb.js.br +0 -0
- package/dist/web/assets/settings-body-B6JGOIYb.js.gz +0 -0
- package/dist/web/assets/settings-store-Br5c1efC.js +0 -2
- package/dist/web/assets/settings-store-Br5c1efC.js.br +0 -0
- package/dist/web/assets/settings-store-Br5c1efC.js.gz +0 -0
- package/dist/web/assets/settings-tab-CIQM4fNE.js +0 -1
- package/dist/web/assets/settings-window-content-BGNNz-E5.js +0 -1
- package/dist/web/assets/sql-query-editor-4iCGE4h2.js.br +0 -3
- package/dist/web/assets/sql-query-editor-4iCGE4h2.js.gz +0 -0
- package/dist/web/assets/sqlite-viewer-CK6DulYk.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-CK6DulYk.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-J9JtvAWV.js +0 -2
- package/dist/web/assets/system-monitor-body-J9JtvAWV.js.br +0 -0
- package/dist/web/assets/system-monitor-body-J9JtvAWV.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-DjBeRE81.js +0 -1
- package/dist/web/assets/system-monitor-window-content-BT7-ugFx.js +0 -1
- package/dist/web/assets/tab-host-window-content-CWBaWKjA.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-CWBaWKjA.js.gz +0 -0
- package/dist/web/assets/team-member-sheet-Br6mrxrw.js.br +0 -0
- package/dist/web/assets/team-member-sheet-Br6mrxrw.js.gz +0 -0
- package/dist/web/assets/team-member-window-content-CwCy7ZWf.js.br +0 -0
- package/dist/web/assets/team-member-window-content-CwCy7ZWf.js.gz +0 -0
- package/dist/web/assets/terminal-tab-BW14RpCb.js.br +0 -0
- package/dist/web/assets/terminal-tab-BW14RpCb.js.gz +0 -0
- package/dist/web/assets/tool-cards-CgOBEUnb.js.br +0 -0
- package/dist/web/assets/tool-cards-CgOBEUnb.js.gz +0 -0
- package/dist/web/assets/use-inline-blame-Bzjfru4n.js +0 -16
- package/dist/web/assets/use-inline-blame-Bzjfru4n.js.br +0 -0
- package/dist/web/assets/use-inline-blame-Bzjfru4n.js.gz +0 -0
- package/dist/web/assets/use-lsp-hGdjL2hV.js +0 -6
- package/dist/web/assets/use-lsp-hGdjL2hV.js.br +0 -0
- package/dist/web/assets/use-lsp-hGdjL2hV.js.gz +0 -0
- package/dist/web/assets/vendor-ui-BHh12_0v.js.br +0 -0
- package/dist/web/assets/vendor-ui-BHh12_0v.js.gz +0 -0
- package/dist/web/assets/video-preview-P9T-ZVdN.js +0 -1
- package/dist/web/assets/video-preview-P9T-ZVdN.js.br +0 -0
- package/dist/web/assets/video-preview-P9T-ZVdN.js.gz +0 -0
- package/dist/web/assets/ws-auth-kXgsX2Js.js +0 -1
- package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
- /package/dist/web/assets/{__vite-browser-external-BpSQWxcq.js → __vite-browser-external-BILfYaAs.js} +0 -0
- /package/dist/web/assets/{run-in-terminal-HIdNgYim.js → run-in-terminal-2pSiup8e.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-DLDtzLib.js → vendor-xterm-DF1JTfsy.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-DLDtzLib.js.br → vendor-xterm-DF1JTfsy.js.br} +0 -0
- /package/dist/web/assets/{vendor-xterm-DLDtzLib.js.gz → vendor-xterm-DF1JTfsy.js.gz} +0 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `Date.now()`, re-read once a minute.
|
|
5
|
+
*
|
|
6
|
+
* Re-read rather than counted: a phone that slept for six hours throttles timers or drops
|
|
7
|
+
* them entirely, so an accumulated tally comes back six hours short — and every caller here
|
|
8
|
+
* is measuring exactly the kind of gap a sleeping device produces.
|
|
9
|
+
*
|
|
10
|
+
* One minute because that is the finest unit any of this is displayed at; a second-resolution
|
|
11
|
+
* tick would re-render the composer sixty times for a label that cannot change.
|
|
12
|
+
*/
|
|
13
|
+
export function useMinuteClock(): number {
|
|
14
|
+
const [now, setNow] = useState(() => Date.now());
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const id = setInterval(() => setNow(Date.now()), 60_000);
|
|
17
|
+
return () => clearInterval(id);
|
|
18
|
+
}, []);
|
|
19
|
+
return now;
|
|
20
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { useCallback, useRef, useState } from "react";
|
|
2
|
+
import { toast } from "sonner";
|
|
3
|
+
import { getAuthToken } from "@/lib/api-client";
|
|
4
|
+
import { recordingToWav } from "@/lib/voice-wav";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The chat mic when Settings has it pointed at Whisper on the PPM host.
|
|
8
|
+
*
|
|
9
|
+
* Same shape as `useVoiceInput` (the browser's own recogniser) so the composer
|
|
10
|
+
* can swap one for the other, with one extra state: Whisper is not streaming,
|
|
11
|
+
* so there is a wait between "stopped talking" and "text appears" that the
|
|
12
|
+
* button has to show — `isTranscribing`.
|
|
13
|
+
*
|
|
14
|
+
* `supported` is about recording, not recognising: any browser with
|
|
15
|
+
* `MediaRecorder` can feed this, which is the point of doing it on the server.
|
|
16
|
+
* Both still need a secure context — `navigator.mediaDevices` does not exist on
|
|
17
|
+
* a plain-HTTP origin, so neither engine can reach the mic there.
|
|
18
|
+
*/
|
|
19
|
+
export function useWhisperVoiceInput(language = "vi") {
|
|
20
|
+
const [isListening, setIsListening] = useState(false);
|
|
21
|
+
const [isTranscribing, setIsTranscribing] = useState(false);
|
|
22
|
+
const recorderRef = useRef<MediaRecorder | null>(null);
|
|
23
|
+
const chunksRef = useRef<Blob[]>([]);
|
|
24
|
+
const cancelledRef = useRef(false);
|
|
25
|
+
|
|
26
|
+
const supported =
|
|
27
|
+
typeof window !== "undefined" &&
|
|
28
|
+
typeof MediaRecorder !== "undefined" &&
|
|
29
|
+
typeof navigator !== "undefined" &&
|
|
30
|
+
!!navigator.mediaDevices?.getUserMedia &&
|
|
31
|
+
typeof OfflineAudioContext !== "undefined";
|
|
32
|
+
|
|
33
|
+
const start = useCallback(
|
|
34
|
+
async (onResult: (text: string, isFinal: boolean) => void) => {
|
|
35
|
+
if (!supported || recorderRef.current) return;
|
|
36
|
+
let stream: MediaStream;
|
|
37
|
+
try {
|
|
38
|
+
stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
39
|
+
} catch {
|
|
40
|
+
toast.error("Microphone unavailable", { description: "Allow microphone access for this site." });
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const recorder = new MediaRecorder(stream);
|
|
45
|
+
recorderRef.current = recorder;
|
|
46
|
+
chunksRef.current = [];
|
|
47
|
+
cancelledRef.current = false;
|
|
48
|
+
|
|
49
|
+
recorder.ondataavailable = (e) => {
|
|
50
|
+
if (e.data.size > 0) chunksRef.current.push(e.data);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
recorder.onstop = async () => {
|
|
54
|
+
// Release the mic before the wait, or the browser keeps its recording
|
|
55
|
+
// indicator lit for the whole transcription.
|
|
56
|
+
for (const track of stream.getTracks()) track.stop();
|
|
57
|
+
recorderRef.current = null;
|
|
58
|
+
setIsListening(false);
|
|
59
|
+
|
|
60
|
+
const chunks = chunksRef.current;
|
|
61
|
+
chunksRef.current = [];
|
|
62
|
+
if (cancelledRef.current || chunks.length === 0) return;
|
|
63
|
+
|
|
64
|
+
setIsTranscribing(true);
|
|
65
|
+
try {
|
|
66
|
+
const wav = await recordingToWav(new Blob(chunks, { type: recorder.mimeType }));
|
|
67
|
+
const text = await postForTranscript(wav, language);
|
|
68
|
+
if (text) onResult(text, true);
|
|
69
|
+
else
|
|
70
|
+
toast.info("Nothing was picked up", {
|
|
71
|
+
description: "Speak closer to the mic, or check which input device is selected.",
|
|
72
|
+
});
|
|
73
|
+
} catch (e) {
|
|
74
|
+
toast.error("Could not transcribe", { description: e instanceof Error ? e.message : String(e) });
|
|
75
|
+
} finally {
|
|
76
|
+
setIsTranscribing(false);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
recorder.start();
|
|
81
|
+
setIsListening(true);
|
|
82
|
+
},
|
|
83
|
+
[supported, language],
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
/** Finish the recording and transcribe it. */
|
|
87
|
+
const stop = useCallback(() => {
|
|
88
|
+
if (recorderRef.current?.state === "recording") recorderRef.current.stop();
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
91
|
+
/** Drop the recording — used when the message is sent while the mic is open. */
|
|
92
|
+
const cancel = useCallback(() => {
|
|
93
|
+
cancelledRef.current = true;
|
|
94
|
+
stop();
|
|
95
|
+
}, [stop]);
|
|
96
|
+
|
|
97
|
+
return { isListening, isTranscribing, start, stop, cancel, supported };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function postForTranscript(wav: Uint8Array, language: string): Promise<string> {
|
|
101
|
+
const token = getAuthToken();
|
|
102
|
+
const res = await fetch(`/api/speech/transcribe?lang=${encodeURIComponent(language)}`, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: {
|
|
105
|
+
"Content-Type": "audio/wav",
|
|
106
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
107
|
+
},
|
|
108
|
+
body: wav as BodyInit,
|
|
109
|
+
});
|
|
110
|
+
const json = (await res.json().catch(() => null)) as { ok?: boolean; data?: { text: string }; error?: string } | null;
|
|
111
|
+
if (!res.ok || !json?.ok) throw new Error(json?.error ?? `HTTP ${res.status}`);
|
|
112
|
+
return json.data?.text ?? "";
|
|
113
|
+
}
|
|
@@ -19,3 +19,22 @@ export const tunnelsApi = {
|
|
|
19
19
|
start: (port: number) => api.post<{ port: number; url: string }>("/api/tunnels", { port }),
|
|
20
20
|
stop: (pid: number) => api.del(`/api/tunnels/${pid}`),
|
|
21
21
|
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* PPM's own public tunnel (`/api/tunnel`) — distinct from the registry above,
|
|
25
|
+
* which lists every cloudflared on the machine.
|
|
26
|
+
*/
|
|
27
|
+
export interface PublicTunnelStatus {
|
|
28
|
+
/** A tunnel is actually serving right now. */
|
|
29
|
+
active: boolean;
|
|
30
|
+
url: string | null;
|
|
31
|
+
localUrl: string | null;
|
|
32
|
+
/** The master switch. Absent on a server older than it, where it was always on. */
|
|
33
|
+
enabled?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const publicTunnelApi = {
|
|
37
|
+
status: () => api.get<PublicTunnelStatus>("/api/tunnel"),
|
|
38
|
+
setEnabled: (enabled: boolean) =>
|
|
39
|
+
api.post<{ enabled: boolean; reload: string }>("/api/tunnel/enabled", { enabled }),
|
|
40
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A repository's branches, as items for the searchable select.
|
|
3
|
+
*
|
|
4
|
+
* Local branches are listed before remote-tracking ones, under their own headings,
|
|
5
|
+
* because that is the order a choice is made in — and because a repository with a
|
|
6
|
+
* few dozen local branches routinely has hundreds of remote ones, and ungrouped the
|
|
7
|
+
* handful anyone is choosing between is lost among them. The label is the whole
|
|
8
|
+
* name, `remotes/origin/` included, so typing `origin` narrows to one remote.
|
|
9
|
+
*/
|
|
10
|
+
import { Cloud, GitBranch as GitBranchIcon } from "@/lib/icons";
|
|
11
|
+
import type { SearchSelectItem } from "./search-select-rows";
|
|
12
|
+
import type { GitBranch } from "../../types/git";
|
|
13
|
+
|
|
14
|
+
function branchItem(branch: GitBranch): SearchSelectItem {
|
|
15
|
+
return {
|
|
16
|
+
value: branch.name,
|
|
17
|
+
label: branch.name,
|
|
18
|
+
group: branch.remote ? "Remote branches" : "Branches",
|
|
19
|
+
icon: branch.remote ? Cloud : GitBranchIcon,
|
|
20
|
+
hint: branch.current ? "current" : undefined,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function branchItems(branches: GitBranch[]): SearchSelectItem[] {
|
|
25
|
+
return [
|
|
26
|
+
...branches.filter((b) => !b.remote),
|
|
27
|
+
...branches.filter((b) => b.remote),
|
|
28
|
+
].map(branchItem);
|
|
29
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which CPU graph the Performance page draws — Mission Center's "Change Top
|
|
3
|
+
* Graph To" menu, ported.
|
|
4
|
+
*
|
|
5
|
+
* Pure and free of store imports on purpose: importing the settings store reads
|
|
6
|
+
* `localStorage` at module scope and throws under `bun:test`, so the parsing and
|
|
7
|
+
* the grid maths would be untestable if they lived beside the component.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const CPU_GRAPH_MODES = ["overall", "logical", "threads", "threads-stacked"] as const;
|
|
11
|
+
export type CpuGraphMode = (typeof CPU_GRAPH_MODES)[number];
|
|
12
|
+
|
|
13
|
+
export const CPU_GRAPH_LABELS: Record<CpuGraphMode, string> = {
|
|
14
|
+
overall: "Overall utilisation",
|
|
15
|
+
logical: "Logical processors",
|
|
16
|
+
threads: "All threads",
|
|
17
|
+
"threads-stacked": "All threads stacked",
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** A mode off localStorage is untrusted the same way one off the wire is. */
|
|
21
|
+
export function parseCpuGraphMode(value: unknown): CpuGraphMode {
|
|
22
|
+
return CPU_GRAPH_MODES.includes(value as CpuGraphMode) ? (value as CpuGraphMode) : "overall";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Mission Center's second, full-width graph under the utilisation one. `none`
|
|
26
|
+
* removes it rather than drawing an empty frame. */
|
|
27
|
+
export const CPU_BOTTOM_GRAPHS = ["temperature", "power", "clock", "none"] as const;
|
|
28
|
+
export type CpuBottomGraph = (typeof CPU_BOTTOM_GRAPHS)[number];
|
|
29
|
+
|
|
30
|
+
export const CPU_BOTTOM_LABELS: Record<CpuBottomGraph, string> = {
|
|
31
|
+
temperature: "Temperature",
|
|
32
|
+
power: "Power draw",
|
|
33
|
+
clock: "Clock speed",
|
|
34
|
+
none: "None",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export function parseCpuBottomGraph(value: unknown): CpuBottomGraph {
|
|
38
|
+
return CPU_BOTTOM_GRAPHS.includes(value as CpuBottomGraph)
|
|
39
|
+
? (value as CpuBottomGraph)
|
|
40
|
+
: "temperature";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Below this a cell is too narrow to read a curve in, so the grid drops a column
|
|
44
|
+
* rather than shrinking past it. 24 threads at Mission Center's six columns needs
|
|
45
|
+
* a ~700px pane; a floating window or a phone routinely has less. */
|
|
46
|
+
export const MIN_CELL_PX = 84;
|
|
47
|
+
|
|
48
|
+
export interface CoreGridLayout {
|
|
49
|
+
cols: number;
|
|
50
|
+
rows: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Mission Center lays the per-thread grid out as `rows = floor(sqrt(n))`, then
|
|
55
|
+
* fills each row — which is what puts a 24-thread i9 in 6 columns of 4 rather
|
|
56
|
+
* than the 5x5 a naive `ceil(sqrt(n))` would give.
|
|
57
|
+
*
|
|
58
|
+
* The width clamp is ours: that layout assumes a window as wide as the screen,
|
|
59
|
+
* and PPM draws this inside a resizable floating window and on a phone. Columns
|
|
60
|
+
* are capped by what fits, never by a breakpoint — the pane's own width is the
|
|
61
|
+
* only thing that knows whether six curves fit side by side.
|
|
62
|
+
*/
|
|
63
|
+
export function coreGridLayout(count: number, widthPx: number): CoreGridLayout {
|
|
64
|
+
if (count <= 0) return { cols: 0, rows: 0 };
|
|
65
|
+
const missionCenterCols = Math.ceil(count / Math.max(1, Math.floor(Math.sqrt(count))));
|
|
66
|
+
// A width of 0 is "not measured yet" (the element has not laid out), not "no
|
|
67
|
+
// room" — clamping to one column there would draw a 24-row column for a frame.
|
|
68
|
+
const fits = widthPx > 0 ? Math.max(1, Math.floor(widthPx / MIN_CELL_PX)) : missionCenterCols;
|
|
69
|
+
const cols = Math.min(missionCenterCols, fits);
|
|
70
|
+
return { cols, rows: Math.ceil(count / cols) };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* How long the visible history window actually spans, for the "over 1 minute"
|
|
75
|
+
* caption Mission Center writes above each graph. Ours is read from the points
|
|
76
|
+
* themselves rather than assumed: PPM's window is 200 points at the server's own
|
|
77
|
+
* cadence, which is 2s on the full tier and 5s on the light one, so a fixed
|
|
78
|
+
* wording would be wrong on one of them and wrong again on a fresh window that
|
|
79
|
+
* has only collected a few seconds.
|
|
80
|
+
*/
|
|
81
|
+
export function historySpanLabel(timestamps: readonly number[]): string | undefined {
|
|
82
|
+
if (timestamps.length < 2) return undefined;
|
|
83
|
+
const first = timestamps[0]!;
|
|
84
|
+
const last = timestamps[timestamps.length - 1]!;
|
|
85
|
+
const seconds = Math.round((last - first) / 1000);
|
|
86
|
+
if (!Number.isFinite(seconds) || seconds <= 0) return undefined;
|
|
87
|
+
if (seconds < 90) return `over ${seconds} seconds`;
|
|
88
|
+
const minutes = Math.round(seconds / 60);
|
|
89
|
+
return minutes === 1 ? "over 1 minute" : `over ${minutes} minutes`;
|
|
90
|
+
}
|
|
@@ -5,6 +5,20 @@ export function formatRam(mb: number): string {
|
|
|
5
5
|
return mb < 1024 ? `${mb.toFixed(0)} MB` : `${(mb / 1024).toFixed(1)} GB`;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
/** A plain byte count — capacities, and the since-boot totals on a device page. */
|
|
9
|
+
export function formatBytes(bytes: number): string {
|
|
10
|
+
if (!Number.isFinite(bytes) || bytes < 0) return "—";
|
|
11
|
+
if (bytes < 1024) return `${bytes.toFixed(0)} B`;
|
|
12
|
+
const units = ["KB", "MB", "GB", "TB", "PB"];
|
|
13
|
+
let value = bytes / 1024;
|
|
14
|
+
let unit = 0;
|
|
15
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
16
|
+
value /= 1024;
|
|
17
|
+
unit++;
|
|
18
|
+
}
|
|
19
|
+
return `${value.toFixed(value < 10 ? 1 : 0)} ${units[unit]}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
8
22
|
/** Bytes/second → human-readable rate, 1 decimal past B/s. */
|
|
9
23
|
export function formatBps(bytesPerSec: number): string {
|
|
10
24
|
if (bytesPerSec < 1024) return `${bytesPerSec.toFixed(0)} B/s`;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decisions behind the branch picker, kept out of the component.
|
|
3
|
+
*
|
|
4
|
+
* Two of them are not presentation at all. `checkoutTarget` decides what git is
|
|
5
|
+
* actually asked to run, and `buildRows` decides what Enter lands on — both are
|
|
6
|
+
* worth a test, and importing the component to reach them would pull in the
|
|
7
|
+
* zustand stores, which read `localStorage` at module scope and throw under
|
|
8
|
+
* `bun:test`.
|
|
9
|
+
*/
|
|
10
|
+
import type { CheckoutMode, GitRef } from "../../types/git";
|
|
11
|
+
|
|
12
|
+
/** The three items above the ref list, in VS Code's order. */
|
|
13
|
+
export type PickerAction = "create" | "create-from" | "detach";
|
|
14
|
+
|
|
15
|
+
export const PICKER_ACTIONS: { action: PickerAction; label: string }[] = [
|
|
16
|
+
{ action: "create", label: "Create new branch..." },
|
|
17
|
+
{ action: "create-from", label: "Create new branch from..." },
|
|
18
|
+
{ action: "detach", label: "Checkout detached..." },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
/** Group headers, and the order the groups appear in. */
|
|
22
|
+
const GROUPS: { type: GitRef["type"]; label: string }[] = [
|
|
23
|
+
{ type: "branch", label: "branches" },
|
|
24
|
+
{ type: "remote", label: "remote branches" },
|
|
25
|
+
{ type: "tag", label: "tags" },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export type PickerRow =
|
|
29
|
+
| { kind: "action"; action: PickerAction; label: string }
|
|
30
|
+
| { kind: "separator"; label: string }
|
|
31
|
+
| { kind: "ref"; ref: GitRef };
|
|
32
|
+
|
|
33
|
+
function matchesRef(ref: GitRef, q: string): boolean {
|
|
34
|
+
return !q || ref.name.toLowerCase().includes(q) || ref.subject.toLowerCase().includes(q);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The visible rows for one query.
|
|
39
|
+
*
|
|
40
|
+
* A group header is emitted only when something survives the filter under it —
|
|
41
|
+
* a "tags" heading over nothing reads as a list that failed to load. The
|
|
42
|
+
* actions are filtered by their own labels, which is what VS Code's quick pick
|
|
43
|
+
* does to every item it is given; `actions: false` is the second stage, where
|
|
44
|
+
* the question is only which ref to branch from or detach at.
|
|
45
|
+
*/
|
|
46
|
+
export function buildRows(
|
|
47
|
+
refs: GitRef[],
|
|
48
|
+
query: string,
|
|
49
|
+
opts: { actions: boolean } = { actions: true },
|
|
50
|
+
): PickerRow[] {
|
|
51
|
+
const q = query.trim().toLowerCase();
|
|
52
|
+
const rows: PickerRow[] = [];
|
|
53
|
+
|
|
54
|
+
if (opts.actions) {
|
|
55
|
+
for (const a of PICKER_ACTIONS) {
|
|
56
|
+
if (!q || a.label.toLowerCase().includes(q)) rows.push({ kind: "action", ...a });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const group of GROUPS) {
|
|
61
|
+
const matched = refs.filter((r) => r.type === group.type && matchesRef(r, q));
|
|
62
|
+
if (!matched.length) continue;
|
|
63
|
+
rows.push({ kind: "separator", label: group.label });
|
|
64
|
+
for (const ref of matched) rows.push({ kind: "ref", ref });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return rows;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The first row Enter may land on, or -1 when the filter matched nothing.
|
|
72
|
+
*
|
|
73
|
+
* Typed on the `kind` alone rather than on `PickerRow`, so the branch select in
|
|
74
|
+
* Branch Review keyboard-navigates its own rows with this same pair instead of
|
|
75
|
+
* a second copy of the wrap-and-skip-headers arithmetic.
|
|
76
|
+
*/
|
|
77
|
+
export function firstSelectable(rows: readonly { kind: string }[]): number {
|
|
78
|
+
return rows.findIndex((r) => r.kind !== "separator");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** Arrow-key movement: wraps, and never stops on a group header. */
|
|
82
|
+
export function moveSelection(rows: readonly { kind: string }[], current: number, step: 1 | -1): number {
|
|
83
|
+
const n = rows.length;
|
|
84
|
+
if (!n) return -1;
|
|
85
|
+
let idx = current;
|
|
86
|
+
for (let i = 0; i < n; i++) {
|
|
87
|
+
idx = (idx + step + n) % n;
|
|
88
|
+
if (rows[idx]!.kind !== "separator") return idx;
|
|
89
|
+
}
|
|
90
|
+
return -1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** `origin/feature/x` → `feature/x`. The remote name is the first segment and only the first. */
|
|
94
|
+
export function localNameFor(remoteName: string): string {
|
|
95
|
+
const slash = remoteName.indexOf("/");
|
|
96
|
+
return slash < 0 ? remoteName : remoteName.slice(slash + 1);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* What picking a ref actually has to run.
|
|
101
|
+
*
|
|
102
|
+
* A remote-tracking ref is not a branch: `git checkout origin/foo` lands on a
|
|
103
|
+
* **detached HEAD** at that commit, which looks like it worked right up until
|
|
104
|
+
* the first commit made on it belongs to no branch. `-t` is what creates the
|
|
105
|
+
* local branch that follows it — but `-t` *fails* when that local branch
|
|
106
|
+
* already exists, and checking the existing one out is what the user meant
|
|
107
|
+
* anyway. Deciding here rather than server-side is deliberate: the answer needs
|
|
108
|
+
* the whole ref list, which the picker already has and the route does not.
|
|
109
|
+
*/
|
|
110
|
+
export function checkoutTarget(ref: GitRef, refs: GitRef[]): { ref: string; mode: CheckoutMode } {
|
|
111
|
+
if (ref.type !== "remote") return { ref: ref.name, mode: "checkout" };
|
|
112
|
+
const local = localNameFor(ref.name);
|
|
113
|
+
const exists = refs.some((r) => r.type === "branch" && r.name === local);
|
|
114
|
+
return exists ? { ref: local, mode: "checkout" } : { ref: ref.name, mode: "track" };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The second line of a row: `thawngho · 7b758e3 · fix(NX-5832): stop retrying…` */
|
|
118
|
+
export function refDetail(ref: GitRef): string {
|
|
119
|
+
return [ref.author, ref.shortHash, ref.subject].filter(Boolean).join(" · ");
|
|
120
|
+
}
|
|
@@ -28,6 +28,10 @@ export interface LspMissingServer {
|
|
|
28
28
|
id: string;
|
|
29
29
|
displayName: string;
|
|
30
30
|
installHint: string;
|
|
31
|
+
/** Whether PPM can install this one on *this host* — a toolchain-based install needs that toolchain. */
|
|
32
|
+
installable?: boolean;
|
|
33
|
+
/** What the install would use, so the dialog can say what pressing the button does. */
|
|
34
|
+
installWith?: "bun" | "go" | "rustup";
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
export type LspDocumentStatus =
|
|
@@ -132,6 +136,22 @@ export class LspConnection {
|
|
|
132
136
|
return this.docs.get(path)?.status;
|
|
133
137
|
}
|
|
134
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Ask again for every document that had no server.
|
|
141
|
+
*
|
|
142
|
+
* What an install leaves to do: the file is already open, and closing and reopening the tab
|
|
143
|
+
* to pick up a server that has just appeared is exactly the step the button exists to remove.
|
|
144
|
+
* Only the documents with nothing serving them, so a tab that already has a server is never
|
|
145
|
+
* taken away from it.
|
|
146
|
+
*/
|
|
147
|
+
retryUnavailable(): void {
|
|
148
|
+
for (const [path, entry] of this.docs) {
|
|
149
|
+
if (entry.status.state !== "unavailable") continue;
|
|
150
|
+
this.setStatus(path, { state: "opening" });
|
|
151
|
+
this.sendOpen(path);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
135
155
|
// ── Requests ────────────────────────────────────────────────────────────
|
|
136
156
|
|
|
137
157
|
/**
|
|
@@ -309,6 +329,18 @@ export function acquireLspConnection(projectName: string): LspConnection {
|
|
|
309
329
|
return connection;
|
|
310
330
|
}
|
|
311
331
|
|
|
332
|
+
/**
|
|
333
|
+
* `retryUnavailable` on every open connection.
|
|
334
|
+
*
|
|
335
|
+
* One install serves every project at once — one `bun add` puts the server where every session
|
|
336
|
+
* looks — and the editors of other projects are still mounted behind the tab pool, so asking
|
|
337
|
+
* only the project whose dialog was open would leave the others claiming a server is missing
|
|
338
|
+
* that is now installed.
|
|
339
|
+
*/
|
|
340
|
+
export function retryUnavailableLspDocuments(): void {
|
|
341
|
+
for (const entry of connections.values()) entry.connection.retryUnavailable();
|
|
342
|
+
}
|
|
343
|
+
|
|
312
344
|
/** True when that was the last holder, so the project has no editor open any more. */
|
|
313
345
|
export function releaseLspConnection(projectName: string): boolean {
|
|
314
346
|
const existing = connections.get(projectName);
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rows a searchable select shows for one query.
|
|
3
|
+
*
|
|
4
|
+
* Pure, and in `lib` rather than beside the component, because importing the
|
|
5
|
+
* component reaches the sheet's hooks, which want a DOM — the decisions here are
|
|
6
|
+
* testable without one.
|
|
7
|
+
*
|
|
8
|
+
* The match is a case-insensitive substring of the whole label, the same one the
|
|
9
|
+
* checkout quick pick makes. Not a prefix: the lists this serves are names that
|
|
10
|
+
* agree for most of their length — `fix/NX-5175-…` branches, `nxsys-backend-nx5833`
|
|
11
|
+
* checkouts — and the part anyone types is the part that differs.
|
|
12
|
+
*
|
|
13
|
+
* A heading is emitted only when something under it survived the filter, the rule
|
|
14
|
+
* `git-ref-picker.ts` follows too: a heading over nothing reads as a list that failed
|
|
15
|
+
* to load. Groups keep the order they first appear in over the *whole* list, so a
|
|
16
|
+
* filter that empties the first group does not reorder the rest.
|
|
17
|
+
*/
|
|
18
|
+
import type { ElementType } from "react";
|
|
19
|
+
|
|
20
|
+
export interface SearchSelectItem {
|
|
21
|
+
value: string;
|
|
22
|
+
/** Shown on the row and on the trigger, and what the filter matches. */
|
|
23
|
+
label: string;
|
|
24
|
+
/** The heading this row is listed under. Items with none get no heading. */
|
|
25
|
+
group?: string;
|
|
26
|
+
icon?: ElementType;
|
|
27
|
+
/** Short muted text at the end of the row — `current`, say. */
|
|
28
|
+
hint?: string;
|
|
29
|
+
/** Tooltip, for when the label is a shortened form of something longer. */
|
|
30
|
+
title?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type SearchSelectRow =
|
|
34
|
+
| { kind: "separator"; label: string }
|
|
35
|
+
| { kind: "item"; item: SearchSelectItem };
|
|
36
|
+
|
|
37
|
+
export function searchRows(items: SearchSelectItem[], query: string): SearchSelectRow[] {
|
|
38
|
+
const q = query.trim().toLowerCase();
|
|
39
|
+
const matched = q ? items.filter((i) => i.label.toLowerCase().includes(q)) : items;
|
|
40
|
+
const rows: SearchSelectRow[] = [];
|
|
41
|
+
|
|
42
|
+
for (const group of new Set(items.map((i) => i.group))) {
|
|
43
|
+
const inGroup = matched.filter((i) => i.group === group);
|
|
44
|
+
if (!inGroup.length) continue;
|
|
45
|
+
if (group !== undefined) rows.push({ kind: "separator", label: group });
|
|
46
|
+
for (const item of inGroup) rows.push({ kind: "item", item });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return rows;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Where a value sits in the rows, or -1 — what opens the list on the current choice. */
|
|
53
|
+
export function rowIndexOf(rows: SearchSelectRow[], value: string): number {
|
|
54
|
+
return rows.findIndex((r) => r.kind === "item" && r.item.value === value);
|
|
55
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temperature display unit — Mission Center's one Preferences setting that changes
|
|
3
|
+
* what a number MEANS rather than how often it is fetched.
|
|
4
|
+
*
|
|
5
|
+
* Every collector reports Celsius, so this is purely a display conversion and the
|
|
6
|
+
* conversion stays here rather than in six detail panels. `undefined` survives
|
|
7
|
+
* untouched: the whole System Monitor treats it as "this host cannot measure
|
|
8
|
+
* that", and a missing sensor must never render as 32 °F.
|
|
9
|
+
*/
|
|
10
|
+
export type TempUnit = "c" | "f";
|
|
11
|
+
|
|
12
|
+
export function toFahrenheit(celsius: number): number {
|
|
13
|
+
return celsius * 9 / 5 + 32;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** A temperature ready to render, or undefined when there is nothing to show. */
|
|
17
|
+
export function formatTemp(celsius: number | undefined, unit: TempUnit): string | undefined {
|
|
18
|
+
if (celsius === undefined || !Number.isFinite(celsius)) return undefined;
|
|
19
|
+
return unit === "f"
|
|
20
|
+
? `${toFahrenheit(celsius).toFixed(1)} °F`
|
|
21
|
+
: `${celsius.toFixed(1)} °C`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A value off localStorage is untrusted the same way one off the wire is. */
|
|
25
|
+
export function parseTempUnit(value: unknown): TempUnit {
|
|
26
|
+
return value === "f" ? "f" : "c";
|
|
27
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning what `MediaRecorder` produced into what whisper.cpp reads.
|
|
3
|
+
*
|
|
4
|
+
* The browser records Opus in WebM (Chrome), Opus in Ogg (Firefox) or AAC in
|
|
5
|
+
* MP4 (Safari); whisper.cpp reads wav, flac, mp3 and Vorbis-ogg — so none of
|
|
6
|
+
* them, and converting on the host would mean requiring ffmpeg there. The
|
|
7
|
+
* browser already has a decoder for its own recording, so the conversion
|
|
8
|
+
* happens here.
|
|
9
|
+
*
|
|
10
|
+
* `decodeAudioData` resamples to the context's rate, which is why the context
|
|
11
|
+
* is created at 16 kHz: that is Whisper's own rate, and it makes the upload a
|
|
12
|
+
* third of what 48 kHz would cost (32 KB per second of speech).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const WHISPER_SAMPLE_RATE = 16_000;
|
|
16
|
+
|
|
17
|
+
/** Average the channels; Whisper takes mono and a stereo mic gains it nothing. */
|
|
18
|
+
export function mixToMono(channels: Float32Array[]): Float32Array {
|
|
19
|
+
if (channels.length === 0) return new Float32Array(0);
|
|
20
|
+
if (channels.length === 1) return channels[0]!;
|
|
21
|
+
const length = channels[0]!.length;
|
|
22
|
+
const out = new Float32Array(length);
|
|
23
|
+
for (let i = 0; i < length; i++) {
|
|
24
|
+
let sum = 0;
|
|
25
|
+
for (const channel of channels) sum += channel[i] ?? 0;
|
|
26
|
+
out[i] = sum / channels.length;
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 16-bit PCM WAV: a 44-byte header and the samples, clamped to [-1, 1). */
|
|
32
|
+
export function encodeWavPcm16(samples: Float32Array, sampleRate: number): Uint8Array {
|
|
33
|
+
const buffer = new ArrayBuffer(44 + samples.length * 2);
|
|
34
|
+
const view = new DataView(buffer);
|
|
35
|
+
const ascii = (offset: number, text: string) => {
|
|
36
|
+
for (let i = 0; i < text.length; i++) view.setUint8(offset + i, text.charCodeAt(i));
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
ascii(0, "RIFF");
|
|
40
|
+
view.setUint32(4, 36 + samples.length * 2, true);
|
|
41
|
+
ascii(8, "WAVE");
|
|
42
|
+
ascii(12, "fmt ");
|
|
43
|
+
view.setUint32(16, 16, true); // PCM header size
|
|
44
|
+
view.setUint16(20, 1, true); // format: PCM
|
|
45
|
+
view.setUint16(22, 1, true); // channels: mono
|
|
46
|
+
view.setUint32(24, sampleRate, true);
|
|
47
|
+
view.setUint32(28, sampleRate * 2, true); // byte rate
|
|
48
|
+
view.setUint16(32, 2, true); // block align
|
|
49
|
+
view.setUint16(34, 16, true); // bits per sample
|
|
50
|
+
ascii(36, "data");
|
|
51
|
+
view.setUint32(40, samples.length * 2, true);
|
|
52
|
+
|
|
53
|
+
for (let i = 0; i < samples.length; i++) {
|
|
54
|
+
const clamped = Math.max(-1, Math.min(1, samples[i]!));
|
|
55
|
+
view.setInt16(44 + i * 2, clamped < 0 ? clamped * 0x8000 : clamped * 0x7fff, true);
|
|
56
|
+
}
|
|
57
|
+
return new Uint8Array(buffer);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Decode a recording and re-encode it as a 16 kHz mono WAV. */
|
|
61
|
+
export async function recordingToWav(blob: Blob): Promise<Uint8Array> {
|
|
62
|
+
const ctx = new OfflineAudioContext(1, 1, WHISPER_SAMPLE_RATE);
|
|
63
|
+
const decoded = await ctx.decodeAudioData(await blob.arrayBuffer());
|
|
64
|
+
const channels: Float32Array[] = [];
|
|
65
|
+
for (let i = 0; i < decoded.numberOfChannels; i++) channels.push(decoded.getChannelData(i));
|
|
66
|
+
// Header takes the buffer's own rate: a browser that declines to resample
|
|
67
|
+
// still produces a correct file, and whisper.cpp resamples it on its side.
|
|
68
|
+
return encodeWavPcm16(mixToMono(channels), decoded.sampleRate);
|
|
69
|
+
}
|