@hienlh/ppm 0.23.20 → 0.23.22

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.
Files changed (466) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/CLAUDE.md +3 -2
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/http-api.md +1 -1
  5. package/dist/web/assets/{accounts-settings-section-BTga1zwH.js → accounts-settings-section-CiNmIj02.js} +1 -1
  6. package/dist/web/assets/accounts-settings-section-CiNmIj02.js.br +0 -0
  7. package/dist/web/assets/accounts-settings-section-CiNmIj02.js.gz +0 -0
  8. package/dist/web/assets/{agent-session-sheet-Q3kasbrZ.js → agent-session-sheet-DRR36PDA.js} +2 -2
  9. package/dist/web/assets/agent-session-sheet-DRR36PDA.js.br +0 -0
  10. package/dist/web/assets/agent-session-sheet-DRR36PDA.js.gz +0 -0
  11. package/dist/web/assets/{agent-session-window-content-UNuEprIK.js → agent-session-window-content-DMhy16BM.js} +1 -1
  12. package/dist/web/assets/agent-session-window-content-DMhy16BM.js.br +0 -0
  13. package/dist/web/assets/agent-session-window-content-DMhy16BM.js.gz +0 -0
  14. package/dist/web/assets/{ai-resource-editor-BEwA6Do0.js → ai-resource-editor-BG8boPw7.js} +1 -1
  15. package/dist/web/assets/ai-resource-editor-BG8boPw7.js.br +0 -0
  16. package/dist/web/assets/ai-resource-editor-BG8boPw7.js.gz +0 -0
  17. package/dist/web/assets/{ai-settings-section-DVKJPM0n.js → ai-settings-section-DMGM1hYx.js} +1 -1
  18. package/dist/web/assets/ai-settings-section-DMGM1hYx.js.br +0 -0
  19. package/dist/web/assets/ai-settings-section-DMGM1hYx.js.gz +0 -0
  20. package/dist/web/assets/{ai-settings-section-DlfY5lFT.js → ai-settings-section-De5dYWlC.js} +1 -1
  21. package/dist/web/assets/{android-tab-BNa6R9fV.js → android-tab-lHDxD93G.js} +1 -1
  22. package/dist/web/assets/android-tab-lHDxD93G.js.br +0 -0
  23. package/dist/web/assets/android-tab-lHDxD93G.js.gz +0 -0
  24. package/dist/web/assets/{appearance-settings-section-CjWFwWNn.js → appearance-settings-section-DO-ZyoMj.js} +1 -1
  25. package/dist/web/assets/appearance-settings-section-DO-ZyoMj.js.br +0 -0
  26. package/dist/web/assets/appearance-settings-section-DO-ZyoMj.js.gz +0 -0
  27. package/dist/web/assets/{apps-panel-D4xjeMN9.js → apps-panel-CH-0CHYY.js} +1 -1
  28. package/dist/web/assets/apps-panel-CH-0CHYY.js.br +0 -0
  29. package/dist/web/assets/apps-panel-CH-0CHYY.js.gz +0 -0
  30. package/dist/web/assets/{branch-review-tab-BCW5294d.js → branch-review-tab-CdGtvekz.js} +1 -1
  31. package/dist/web/assets/branch-review-tab-CdGtvekz.js.br +0 -0
  32. package/dist/web/assets/branch-review-tab-CdGtvekz.js.gz +0 -0
  33. package/dist/web/assets/{chat-tab-BG0M1D3d.js → chat-tab-DMqWnFIo.js} +1 -1
  34. package/dist/web/assets/chat-tab-DMqWnFIo.js.br +0 -0
  35. package/dist/web/assets/chat-tab-DMqWnFIo.js.gz +0 -0
  36. package/dist/web/assets/{chat-tab-DsW93LTL.js → chat-tab-DzWpwPFb.js} +4 -4
  37. package/dist/web/assets/chat-tab-DzWpwPFb.js.br +0 -0
  38. package/dist/web/assets/chat-tab-DzWpwPFb.js.gz +0 -0
  39. package/dist/web/assets/{code-editor-AxLIpK-b.js → code-editor-BCv8ONNS.js} +4 -4
  40. package/dist/web/assets/code-editor-BCv8ONNS.js.br +0 -0
  41. package/dist/web/assets/code-editor-BCv8ONNS.js.gz +0 -0
  42. package/dist/web/assets/{codex-reset-credit-button-BnRmkBZu.js → codex-reset-credit-button-CGMbIHak.js} +1 -1
  43. package/dist/web/assets/codex-reset-credit-button-CGMbIHak.js.br +0 -0
  44. package/dist/web/assets/codex-reset-credit-button-CGMbIHak.js.gz +0 -0
  45. package/dist/web/assets/{conflict-editor-BwuNUPAK.js → conflict-editor-3WmzqBlW.js} +1 -1
  46. package/dist/web/assets/conflict-editor-3WmzqBlW.js.br +0 -0
  47. package/dist/web/assets/conflict-editor-3WmzqBlW.js.gz +0 -0
  48. package/dist/web/assets/{connection-form-tab-CM2o80eB.js → connection-form-tab-BoWucvAe.js} +1 -1
  49. package/dist/web/assets/connection-form-tab-BoWucvAe.js.br +0 -0
  50. package/dist/web/assets/connection-form-tab-BoWucvAe.js.gz +0 -0
  51. package/dist/web/assets/{csv-preview-C7ErHm4U.js → csv-preview-BCgGsy7F.js} +1 -1
  52. package/dist/web/assets/csv-preview-BCgGsy7F.js.br +0 -0
  53. package/dist/web/assets/csv-preview-BCgGsy7F.js.gz +0 -0
  54. package/dist/web/assets/{db-tab-parts-C8jbkfxO.js → db-tab-parts-o6QMHBbA.js} +1 -1
  55. package/dist/web/assets/db-tab-parts-o6QMHBbA.js.br +0 -0
  56. package/dist/web/assets/db-tab-parts-o6QMHBbA.js.gz +0 -0
  57. package/dist/web/assets/{design-settings-section-BcGEY4Zf.js → design-settings-section-seo6SdSk.js} +1 -1
  58. package/dist/web/assets/design-settings-section-seo6SdSk.js.br +0 -0
  59. package/dist/web/assets/design-settings-section-seo6SdSk.js.gz +0 -0
  60. package/dist/web/assets/{design-tab-BGPYNFvy.js → design-tab-B-f_b2LM.js} +1 -1
  61. package/dist/web/assets/design-tab-B-f_b2LM.js.br +0 -0
  62. package/dist/web/assets/design-tab-B-f_b2LM.js.gz +0 -0
  63. package/dist/web/assets/{designs-sidebar-panel-BgMj7xRm.js → designs-sidebar-panel-CVT_TQP7.js} +1 -1
  64. package/dist/web/assets/designs-sidebar-panel-CVT_TQP7.js.br +0 -0
  65. package/dist/web/assets/designs-sidebar-panel-CVT_TQP7.js.gz +0 -0
  66. package/dist/web/assets/{diff-viewer-BKJkesHk.js → diff-viewer-y9uBp_8u.js} +1 -1
  67. package/dist/web/assets/diff-viewer-y9uBp_8u.js.br +0 -0
  68. package/dist/web/assets/diff-viewer-y9uBp_8u.js.gz +0 -0
  69. package/dist/web/assets/{docx-preview-DD5oD_nL.js → docx-preview-Dn_x3Inm.js} +1 -1
  70. package/dist/web/assets/docx-preview-Dn_x3Inm.js.br +0 -0
  71. package/dist/web/assets/docx-preview-Dn_x3Inm.js.gz +0 -0
  72. package/dist/web/assets/{editor-dialog--07lKJxl.js → editor-dialog-BwNXSajD.js} +1 -1
  73. package/dist/web/assets/editor-dialog-BwNXSajD.js.br +0 -0
  74. package/dist/web/assets/editor-dialog-BwNXSajD.js.gz +0 -0
  75. package/dist/web/assets/{editor-language-service-51xjO5d3.js → editor-language-service-XA_TnPLN.js} +1 -1
  76. package/dist/web/assets/editor-language-service-XA_TnPLN.js.br +0 -0
  77. package/dist/web/assets/editor-language-service-XA_TnPLN.js.gz +0 -0
  78. package/dist/web/assets/{explorer-body-BXK5SjJQ.js → explorer-body-DukR7zM9.js} +1 -1
  79. package/dist/web/assets/explorer-body-DukR7zM9.js.br +0 -0
  80. package/dist/web/assets/explorer-body-DukR7zM9.js.gz +0 -0
  81. package/dist/web/assets/{explorer-window-content-DKXcCmuX.js → explorer-window-content-BjTZ44S3.js} +1 -1
  82. package/dist/web/assets/explorer-window-content-BjTZ44S3.js.br +0 -0
  83. package/dist/web/assets/explorer-window-content-BjTZ44S3.js.gz +0 -0
  84. package/dist/web/assets/{extension-manager-section-CTpQP6te.js → extension-manager-section-C4WD1BPQ.js} +1 -1
  85. package/dist/web/assets/extension-manager-section-C4WD1BPQ.js.br +0 -0
  86. package/dist/web/assets/extension-manager-section-C4WD1BPQ.js.gz +0 -0
  87. package/dist/web/assets/{extension-webview-COT-yLrM.js → extension-webview-CV2ujflc.js} +1 -1
  88. package/dist/web/assets/extension-webview-CV2ujflc.js.br +0 -0
  89. package/dist/web/assets/extension-webview-CV2ujflc.js.gz +0 -0
  90. package/dist/web/assets/{files-settings-section-CgVR0fqi.js → files-settings-section-hS5ixQ8o.js} +1 -1
  91. package/dist/web/assets/files-settings-section-hS5ixQ8o.js.br +0 -0
  92. package/dist/web/assets/files-settings-section-hS5ixQ8o.js.gz +0 -0
  93. package/dist/web/assets/{general-settings-section-BEMIbE4Y.js → general-settings-section-BnKYY0AA.js} +1 -1
  94. package/dist/web/assets/general-settings-section-BnKYY0AA.js.br +0 -0
  95. package/dist/web/assets/general-settings-section-BnKYY0AA.js.gz +0 -0
  96. package/dist/web/assets/{git-log-panel-Cp2174iI.js → git-log-panel-JiWfR5xv.js} +1 -1
  97. package/dist/web/assets/git-log-panel-JiWfR5xv.js.br +0 -0
  98. package/dist/web/assets/git-log-panel-JiWfR5xv.js.gz +0 -0
  99. package/dist/web/assets/{git-review-tab-CpMJ0qDi.js → git-review-tab-DgvcY__Q.js} +1 -1
  100. package/dist/web/assets/git-review-tab-DgvcY__Q.js.br +0 -0
  101. package/dist/web/assets/git-review-tab-DgvcY__Q.js.gz +0 -0
  102. package/dist/web/assets/{glide-data-grid-BAhwWUhO.js → glide-data-grid-r1MKxDjz.js} +1 -1
  103. package/dist/web/assets/glide-data-grid-r1MKxDjz.js.br +0 -0
  104. package/dist/web/assets/glide-data-grid-r1MKxDjz.js.gz +0 -0
  105. package/dist/web/assets/{grid-save-dialog-DmJhaWOl.js → grid-save-dialog-Fa0yA-Ep.js} +1 -1
  106. package/dist/web/assets/grid-save-dialog-Fa0yA-Ep.js.br +0 -0
  107. package/dist/web/assets/grid-save-dialog-Fa0yA-Ep.js.gz +0 -0
  108. package/dist/web/assets/{group-chat-tab-Bq7-Yegf.js → group-chat-tab-B2Q8yqSX.js} +1 -1
  109. package/dist/web/assets/group-chat-tab-B2Q8yqSX.js.br +0 -0
  110. package/dist/web/assets/group-chat-tab-B2Q8yqSX.js.gz +0 -0
  111. package/dist/web/assets/{impexp-tab-COx3Cvs7.js → impexp-tab-B8Ht4l32.js} +1 -1
  112. package/dist/web/assets/impexp-tab-B8Ht4l32.js.br +0 -0
  113. package/dist/web/assets/impexp-tab-B8Ht4l32.js.gz +0 -0
  114. package/dist/web/assets/{index-BGk8EFQb.js → index-BtAn_krv.js} +5 -5
  115. package/dist/web/assets/index-BtAn_krv.js.br +0 -0
  116. package/dist/web/assets/index-BtAn_krv.js.gz +0 -0
  117. package/dist/web/assets/{jira-watcher-section-BEPbsi62.js → jira-watcher-section--ibKZfBs.js} +1 -1
  118. package/dist/web/assets/{keybindings-store-DzcSIJUF.js → keybindings-store-Cwxl4tHv.js} +1 -1
  119. package/dist/web/assets/{keyboard-shortcuts-section-Bqg7AZiZ.js → keyboard-shortcuts-section-aoiLpWwf.js} +1 -1
  120. package/dist/web/assets/keyboard-shortcuts-section-aoiLpWwf.js.br +0 -0
  121. package/dist/web/assets/keyboard-shortcuts-section-aoiLpWwf.js.gz +0 -0
  122. package/dist/web/assets/{language-servers-section-CJ47DSIA.js → language-servers-section-BkGAezo6.js} +2 -2
  123. package/dist/web/assets/language-servers-section-BkGAezo6.js.br +0 -0
  124. package/dist/web/assets/language-servers-section-BkGAezo6.js.gz +0 -0
  125. package/dist/web/assets/{logs-app-BK-n-y_K.js → logs-app-DYgPtmjm.js} +1 -1
  126. package/dist/web/assets/logs-app-DYgPtmjm.js.br +0 -0
  127. package/dist/web/assets/logs-app-DYgPtmjm.js.gz +0 -0
  128. package/dist/web/assets/{logs-tab-B84EZJ1b.js → logs-tab-BbDosK63.js} +1 -1
  129. package/dist/web/assets/{logs-window-content-B1uN_3pN.js → logs-window-content-CEXAyv-f.js} +1 -1
  130. package/dist/web/assets/{lsp-client-DHSaAyJW.js → lsp-client-CIcphNmU.js} +1 -1
  131. package/dist/web/assets/lsp-client-CIcphNmU.js.br +0 -0
  132. package/dist/web/assets/lsp-client-CIcphNmU.js.gz +0 -0
  133. package/dist/web/assets/{markdown-renderer-BjDQUqT-.js → markdown-renderer-BNzQeoAO.js} +1 -1
  134. package/dist/web/assets/{markdown-renderer-CpxdNG0f.js → markdown-renderer-dk1w3goH.js} +1 -1
  135. package/dist/web/assets/markdown-renderer-dk1w3goH.js.br +0 -0
  136. package/dist/web/assets/markdown-renderer-dk1w3goH.js.gz +0 -0
  137. package/dist/web/assets/{mobile-explorer-sheet-BBwXGmXA.js → mobile-explorer-sheet-D4lPZQZc.js} +1 -1
  138. package/dist/web/assets/mobile-explorer-sheet-D4lPZQZc.js.br +0 -0
  139. package/dist/web/assets/mobile-explorer-sheet-D4lPZQZc.js.gz +0 -0
  140. package/dist/web/assets/{new-design-dialog-B_ZOvGhR.js → new-design-dialog-CIq7xspW.js} +1 -1
  141. package/dist/web/assets/new-design-dialog-CIq7xspW.js.br +0 -0
  142. package/dist/web/assets/new-design-dialog-CIq7xspW.js.gz +0 -0
  143. package/dist/web/assets/{notification-store-HuMqfadD.js → notification-store-Rv36noYX.js} +1 -1
  144. package/dist/web/assets/notifications-settings-section-CoWMtXCC.js +1 -0
  145. package/dist/web/assets/notifications-settings-section-CoWMtXCC.js.br +0 -0
  146. package/dist/web/assets/notifications-settings-section-CoWMtXCC.js.gz +0 -0
  147. package/dist/web/assets/{ppmbot-settings-section-JDvyI-gZ.js → ppmbot-settings-section-Ct0UwA6T.js} +1 -1
  148. package/dist/web/assets/ppmbot-settings-section-Ct0UwA6T.js.br +0 -0
  149. package/dist/web/assets/ppmbot-settings-section-Ct0UwA6T.js.gz +0 -0
  150. package/dist/web/assets/{problems-panel-Bsv_L6T2.js → problems-panel-D-HT-0PE.js} +1 -1
  151. package/dist/web/assets/problems-panel-D-HT-0PE.js.br +0 -0
  152. package/dist/web/assets/problems-panel-D-HT-0PE.js.gz +0 -0
  153. package/dist/web/assets/{process-table-3kZyWOJx.js → process-table-CXN0s4Pq.js} +1 -1
  154. package/dist/web/assets/process-table-CXN0s4Pq.js.br +0 -0
  155. package/dist/web/assets/process-table-CXN0s4Pq.js.gz +0 -0
  156. package/dist/web/assets/{proxy-settings-section-CYcmIp7W.js → proxy-settings-section-e6WWzP8n.js} +1 -1
  157. package/dist/web/assets/proxy-settings-section-e6WWzP8n.js.br +0 -0
  158. package/dist/web/assets/proxy-settings-section-e6WWzP8n.js.gz +0 -0
  159. package/dist/web/assets/{query-tab-CjrWnQUl.js → query-tab-C7C67t8F.js} +1 -1
  160. package/dist/web/assets/query-tab-C7C67t8F.js.br +0 -0
  161. package/dist/web/assets/query-tab-C7C67t8F.js.gz +0 -0
  162. package/dist/web/assets/{remote-access-settings-section-iQkakiVx.js → remote-access-settings-section-gGoATCF9.js} +1 -1
  163. package/dist/web/assets/remote-access-settings-section-gGoATCF9.js.br +0 -0
  164. package/dist/web/assets/remote-access-settings-section-gGoATCF9.js.gz +0 -0
  165. package/dist/web/assets/{remote-desktop-clipboard-notice-CFLnb7ML.js → remote-desktop-clipboard-notice-D5KjvMZx.js} +1 -1
  166. package/dist/web/assets/remote-desktop-clipboard-notice-D5KjvMZx.js.br +0 -0
  167. package/dist/web/assets/remote-desktop-clipboard-notice-D5KjvMZx.js.gz +0 -0
  168. package/dist/web/assets/{remote-desktop-mobile-sheet-C_-_-Nkz.js → remote-desktop-mobile-sheet-CY_DsGXZ.js} +2 -2
  169. package/dist/web/assets/remote-desktop-mobile-sheet-CY_DsGXZ.js.br +0 -0
  170. package/dist/web/assets/remote-desktop-mobile-sheet-CY_DsGXZ.js.gz +0 -0
  171. package/dist/web/assets/{remote-desktop-mobile-view-BnHSrind.js → remote-desktop-mobile-view-DdpI-RF8.js} +1 -1
  172. package/dist/web/assets/remote-desktop-mobile-view-DdpI-RF8.js.br +0 -0
  173. package/dist/web/assets/remote-desktop-mobile-view-DdpI-RF8.js.gz +0 -0
  174. package/dist/web/assets/{remote-desktop-readiness-gate-BGHJ7Sv6.js → remote-desktop-readiness-gate-WIyCCAv5.js} +1 -1
  175. package/dist/web/assets/remote-desktop-readiness-gate-WIyCCAv5.js.br +0 -0
  176. package/dist/web/assets/remote-desktop-readiness-gate-WIyCCAv5.js.gz +0 -0
  177. package/dist/web/assets/{remote-desktop-window-content-BBm1YwM8.js → remote-desktop-window-content-BMORZLZt.js} +1 -1
  178. package/dist/web/assets/remote-desktop-window-content-BMORZLZt.js.br +0 -0
  179. package/dist/web/assets/remote-desktop-window-content-BMORZLZt.js.gz +0 -0
  180. package/dist/web/assets/{rename-object-dialog-BLnF9xyn.js → rename-object-dialog-pIgWe_AL.js} +1 -1
  181. package/dist/web/assets/rename-object-dialog-pIgWe_AL.js.br +0 -0
  182. package/dist/web/assets/rename-object-dialog-pIgWe_AL.js.gz +0 -0
  183. package/dist/web/assets/{review-code-DG7sCIzr.js → review-code-oTNcTYlI.js} +1 -1
  184. package/dist/web/assets/review-code-oTNcTYlI.js.br +0 -0
  185. package/dist/web/assets/review-code-oTNcTYlI.js.gz +0 -0
  186. package/dist/web/assets/{save-as-dialog-KW7cErWh.js → save-as-dialog-CnPLGTaL.js} +1 -1
  187. package/dist/web/assets/save-as-dialog-CnPLGTaL.js.br +0 -0
  188. package/dist/web/assets/save-as-dialog-CnPLGTaL.js.gz +0 -0
  189. package/dist/web/assets/{schedules-settings-section-B4AV4ORv.js → schedules-settings-section-CREqHG_i.js} +1 -1
  190. package/dist/web/assets/schedules-settings-section-CREqHG_i.js.br +0 -0
  191. package/dist/web/assets/schedules-settings-section-CREqHG_i.js.gz +0 -0
  192. package/dist/web/assets/{session-review-tab-C5nPyBzo.js → session-review-tab-nFVJYYJH.js} +1 -1
  193. package/dist/web/assets/session-review-tab-nFVJYYJH.js.br +0 -0
  194. package/dist/web/assets/session-review-tab-nFVJYYJH.js.gz +0 -0
  195. package/dist/web/assets/{settings-body-xWSmRXUD.js → settings-body-DNzH0wOQ.js} +1 -1
  196. package/dist/web/assets/settings-body-DNzH0wOQ.js.br +0 -0
  197. package/dist/web/assets/settings-body-DNzH0wOQ.js.gz +0 -0
  198. package/dist/web/assets/{settings-rows-ChKBHk07.js → settings-rows-D02_4sDY.js} +1 -1
  199. package/dist/web/assets/settings-rows-D02_4sDY.js.br +0 -0
  200. package/dist/web/assets/settings-rows-D02_4sDY.js.gz +0 -0
  201. package/dist/web/assets/settings-section-content-BYW_9mHv.js +1 -0
  202. package/dist/web/assets/{settings-section-content-DdVdyPVX.js → settings-section-content-DWopMfmq.js} +2 -2
  203. package/dist/web/assets/settings-section-content-DWopMfmq.js.br +0 -0
  204. package/dist/web/assets/settings-section-content-DWopMfmq.js.gz +0 -0
  205. package/dist/web/assets/{settings-tab-CzaBBNoj.js → settings-tab-Cg0v5kO7.js} +1 -1
  206. package/dist/web/assets/{settings-window-content-DAvWSu_O.js → settings-window-content-CqhwxJJn.js} +1 -1
  207. package/dist/web/assets/{slash-command-picker-Cn2Fdz2p.js → slash-command-picker-Bcizxvbh.js} +1 -1
  208. package/dist/web/assets/slash-command-picker-Bcizxvbh.js.br +0 -0
  209. package/dist/web/assets/slash-command-picker-Bcizxvbh.js.gz +0 -0
  210. package/dist/web/assets/{sql-object-tab-_mPRCvD9.js → sql-object-tab-b1Z2zVBm.js} +1 -1
  211. package/dist/web/assets/sql-object-tab-b1Z2zVBm.js.br +0 -0
  212. package/dist/web/assets/sql-object-tab-b1Z2zVBm.js.gz +0 -0
  213. package/dist/web/assets/{sqlite-viewer-B8VZt-mA.js → sqlite-viewer-DhPv28tq.js} +1 -1
  214. package/dist/web/assets/sqlite-viewer-DhPv28tq.js.br +0 -0
  215. package/dist/web/assets/sqlite-viewer-DhPv28tq.js.gz +0 -0
  216. package/dist/web/assets/{structure-save-dialog-Lcst7kAl.js → structure-save-dialog-sGUpqs8D.js} +1 -1
  217. package/dist/web/assets/structure-save-dialog-sGUpqs8D.js.br +0 -0
  218. package/dist/web/assets/structure-save-dialog-sGUpqs8D.js.gz +0 -0
  219. package/dist/web/assets/{structure-tab-ljJCzxUe.js → structure-tab-Bo2OmDOc.js} +1 -1
  220. package/dist/web/assets/structure-tab-Bo2OmDOc.js.br +0 -0
  221. package/dist/web/assets/structure-tab-Bo2OmDOc.js.gz +0 -0
  222. package/dist/web/assets/{system-monitor-body-CTi5xUZz.js → system-monitor-body-C475Et9u.js} +2 -2
  223. package/dist/web/assets/system-monitor-body-C475Et9u.js.br +0 -0
  224. package/dist/web/assets/system-monitor-body-C475Et9u.js.gz +0 -0
  225. package/dist/web/assets/{system-monitor-tab-DeL2YoXO.js → system-monitor-tab-Cm52yoW8.js} +1 -1
  226. package/dist/web/assets/{system-monitor-window-content-DK-rQIZh.js → system-monitor-window-content-YYsGHxZF.js} +1 -1
  227. package/dist/web/assets/{tab-host-window-content-CG0vPTc_.js → tab-host-window-content-BKI6QoDa.js} +1 -1
  228. package/dist/web/assets/tab-host-window-content-BKI6QoDa.js.br +0 -0
  229. package/dist/web/assets/tab-host-window-content-BKI6QoDa.js.gz +0 -0
  230. package/dist/web/assets/{table-tab-CTZ3nCWV.js → table-tab-D-nKWMJR.js} +1 -1
  231. package/dist/web/assets/table-tab-D-nKWMJR.js.br +0 -0
  232. package/dist/web/assets/table-tab-D-nKWMJR.js.gz +0 -0
  233. package/dist/web/assets/{telegram-bot-token-field-DF7zq7_h.js → telegram-bot-token-field-BeNS2X2k.js} +1 -1
  234. package/dist/web/assets/telegram-bot-token-field-BeNS2X2k.js.br +0 -0
  235. package/dist/web/assets/telegram-bot-token-field-BeNS2X2k.js.gz +0 -0
  236. package/dist/web/assets/{terminal-tab-B_jECWTH.js → terminal-tab-BbP753Ab.js} +1 -1
  237. package/dist/web/assets/terminal-tab-BbP753Ab.js.br +0 -0
  238. package/dist/web/assets/terminal-tab-BbP753Ab.js.gz +0 -0
  239. package/dist/web/assets/{tool-cards-CsD_VXcG.js → tool-cards-Dh8Ekn2W.js} +2 -2
  240. package/dist/web/assets/tool-cards-Dh8Ekn2W.js.br +0 -0
  241. package/dist/web/assets/tool-cards-Dh8Ekn2W.js.gz +0 -0
  242. package/dist/web/assets/{tools-settings-section-DbBr0Hnr.js → tools-settings-section-DGZRIULE.js} +1 -1
  243. package/dist/web/assets/tools-settings-section-DGZRIULE.js.br +0 -0
  244. package/dist/web/assets/tools-settings-section-DGZRIULE.js.gz +0 -0
  245. package/dist/web/assets/{use-inline-blame-BA2mrMxT.js → use-inline-blame-BxwM-Wkj.js} +1 -1
  246. package/dist/web/assets/use-inline-blame-BxwM-Wkj.js.br +0 -0
  247. package/dist/web/assets/use-inline-blame-BxwM-Wkj.js.gz +0 -0
  248. package/dist/web/assets/{use-lsp-ChbrzwvS.js → use-lsp-BqvwRKmM.js} +1 -1
  249. package/dist/web/assets/use-lsp-BqvwRKmM.js.br +0 -0
  250. package/dist/web/assets/use-lsp-BqvwRKmM.js.gz +0 -0
  251. package/dist/web/assets/{use-monaco-theme-C3_wfz5J.js → use-monaco-theme-DU0EpDHH.js} +1 -1
  252. package/dist/web/assets/{use-process-kill-C-YetnGd.js → use-process-kill-CVN_c4KF.js} +1 -1
  253. package/dist/web/assets/use-process-kill-CVN_c4KF.js.br +0 -0
  254. package/dist/web/assets/use-process-kill-CVN_c4KF.js.gz +0 -0
  255. package/dist/web/assets/{use-sql-schema-info-pKwLD9wk.js → use-sql-schema-info-C0wBmR6-.js} +1 -1
  256. package/dist/web/assets/use-sql-schema-info-C0wBmR6-.js.br +0 -0
  257. package/dist/web/assets/use-sql-schema-info-C0wBmR6-.js.gz +0 -0
  258. package/dist/web/assets/{use-websocket-BFnUq0-x.js → use-websocket-K0OUXbbY.js} +1 -1
  259. package/dist/web/assets/{web-preview-tab-DGLPuZYM.js → web-preview-tab-DOgLv8Gt.js} +1 -1
  260. package/dist/web/assets/web-preview-tab-DOgLv8Gt.js.br +0 -0
  261. package/dist/web/assets/web-preview-tab-DOgLv8Gt.js.gz +0 -0
  262. package/dist/web/index.html +1 -1
  263. package/dist/web/index.html.br +0 -0
  264. package/dist/web/index.html.gz +0 -0
  265. package/dist/web/sw.js +1 -1
  266. package/dist/web/sw.js.br +0 -0
  267. package/dist/web/sw.js.gz +0 -0
  268. package/docs/deployment-guide.md +39 -0
  269. package/package.json +1 -1
  270. package/scripts/mem-diag-report.ts +31 -0
  271. package/src/providers/registry.ts +10 -1
  272. package/src/server/index.ts +7 -6
  273. package/src/server/middleware/access-log.ts +3 -0
  274. package/src/server/middleware/cors-headers.ts +44 -0
  275. package/src/server/routes/chat.ts +4 -1
  276. package/src/server/routes/database-impexp.ts +1 -1
  277. package/src/server/routes/design-preview.ts +2 -2
  278. package/src/server/ws/chat.ts +3 -0
  279. package/src/services/compact-segment.ts +14 -19
  280. package/src/services/database/grid-export-tickets.ts +6 -2
  281. package/src/services/database/impexp/readers/file-text.ts +6 -3
  282. package/src/services/edge-forwarder.ts +12 -0
  283. package/src/services/file-chunks.ts +39 -0
  284. package/src/services/file-lines.ts +10 -17
  285. package/src/services/lsp/lsp-manager.ts +3 -0
  286. package/src/services/memory-diagnostics-native.ts +110 -0
  287. package/src/services/memory-diagnostics-report.ts +200 -0
  288. package/src/services/memory-diagnostics-requests.ts +95 -0
  289. package/src/services/memory-diagnostics-spawns.ts +242 -0
  290. package/src/services/memory-diagnostics.ts +121 -23
  291. package/src/services/memory-watchdog.ts +185 -0
  292. package/src/services/nested-subagent-spy.ts +3 -0
  293. package/src/services/process-thread-snapshot.ts +12 -2
  294. package/src/services/process-tree-memory.ts +161 -0
  295. package/src/services/read-lines.ts +16 -27
  296. package/src/services/supervisor.ts +1 -0
  297. package/src/services/terminal.service.ts +8 -0
  298. package/src/services/transcript-images-file.ts +7 -11
  299. package/src/web/components/chat/question-card.tsx +10 -4
  300. package/src/web/components/settings/notifications-settings-section.tsx +14 -1
  301. package/src/web/lib/web-push-client.ts +35 -5
  302. package/src/web/lib/web-push-support.ts +57 -4
  303. package/dist/web/assets/accounts-settings-section-BTga1zwH.js.br +0 -0
  304. package/dist/web/assets/accounts-settings-section-BTga1zwH.js.gz +0 -0
  305. package/dist/web/assets/agent-session-sheet-Q3kasbrZ.js.br +0 -0
  306. package/dist/web/assets/agent-session-sheet-Q3kasbrZ.js.gz +0 -0
  307. package/dist/web/assets/agent-session-window-content-UNuEprIK.js.br +0 -0
  308. package/dist/web/assets/agent-session-window-content-UNuEprIK.js.gz +0 -0
  309. package/dist/web/assets/ai-resource-editor-BEwA6Do0.js.br +0 -0
  310. package/dist/web/assets/ai-resource-editor-BEwA6Do0.js.gz +0 -0
  311. package/dist/web/assets/ai-settings-section-DVKJPM0n.js.br +0 -0
  312. package/dist/web/assets/ai-settings-section-DVKJPM0n.js.gz +0 -0
  313. package/dist/web/assets/android-tab-BNa6R9fV.js.br +0 -0
  314. package/dist/web/assets/android-tab-BNa6R9fV.js.gz +0 -0
  315. package/dist/web/assets/appearance-settings-section-CjWFwWNn.js.br +0 -0
  316. package/dist/web/assets/appearance-settings-section-CjWFwWNn.js.gz +0 -0
  317. package/dist/web/assets/apps-panel-D4xjeMN9.js.br +0 -0
  318. package/dist/web/assets/apps-panel-D4xjeMN9.js.gz +0 -0
  319. package/dist/web/assets/branch-review-tab-BCW5294d.js.br +0 -0
  320. package/dist/web/assets/branch-review-tab-BCW5294d.js.gz +0 -0
  321. package/dist/web/assets/chat-tab-BG0M1D3d.js.br +0 -3
  322. package/dist/web/assets/chat-tab-BG0M1D3d.js.gz +0 -0
  323. package/dist/web/assets/chat-tab-DsW93LTL.js.br +0 -0
  324. package/dist/web/assets/chat-tab-DsW93LTL.js.gz +0 -0
  325. package/dist/web/assets/code-editor-AxLIpK-b.js.br +0 -0
  326. package/dist/web/assets/code-editor-AxLIpK-b.js.gz +0 -0
  327. package/dist/web/assets/codex-reset-credit-button-BnRmkBZu.js.br +0 -0
  328. package/dist/web/assets/codex-reset-credit-button-BnRmkBZu.js.gz +0 -0
  329. package/dist/web/assets/conflict-editor-BwuNUPAK.js.br +0 -0
  330. package/dist/web/assets/conflict-editor-BwuNUPAK.js.gz +0 -0
  331. package/dist/web/assets/connection-form-tab-CM2o80eB.js.br +0 -0
  332. package/dist/web/assets/connection-form-tab-CM2o80eB.js.gz +0 -0
  333. package/dist/web/assets/csv-preview-C7ErHm4U.js.br +0 -0
  334. package/dist/web/assets/csv-preview-C7ErHm4U.js.gz +0 -0
  335. package/dist/web/assets/db-tab-parts-C8jbkfxO.js.br +0 -0
  336. package/dist/web/assets/db-tab-parts-C8jbkfxO.js.gz +0 -0
  337. package/dist/web/assets/design-settings-section-BcGEY4Zf.js.br +0 -0
  338. package/dist/web/assets/design-settings-section-BcGEY4Zf.js.gz +0 -0
  339. package/dist/web/assets/design-tab-BGPYNFvy.js.br +0 -0
  340. package/dist/web/assets/design-tab-BGPYNFvy.js.gz +0 -0
  341. package/dist/web/assets/designs-sidebar-panel-BgMj7xRm.js.br +0 -0
  342. package/dist/web/assets/designs-sidebar-panel-BgMj7xRm.js.gz +0 -0
  343. package/dist/web/assets/diff-viewer-BKJkesHk.js.br +0 -0
  344. package/dist/web/assets/diff-viewer-BKJkesHk.js.gz +0 -0
  345. package/dist/web/assets/docx-preview-DD5oD_nL.js.br +0 -0
  346. package/dist/web/assets/docx-preview-DD5oD_nL.js.gz +0 -0
  347. package/dist/web/assets/editor-dialog--07lKJxl.js.br +0 -0
  348. package/dist/web/assets/editor-dialog--07lKJxl.js.gz +0 -0
  349. package/dist/web/assets/editor-language-service-51xjO5d3.js.br +0 -0
  350. package/dist/web/assets/editor-language-service-51xjO5d3.js.gz +0 -0
  351. package/dist/web/assets/explorer-body-BXK5SjJQ.js.br +0 -0
  352. package/dist/web/assets/explorer-body-BXK5SjJQ.js.gz +0 -0
  353. package/dist/web/assets/explorer-window-content-DKXcCmuX.js.br +0 -0
  354. package/dist/web/assets/explorer-window-content-DKXcCmuX.js.gz +0 -0
  355. package/dist/web/assets/extension-manager-section-CTpQP6te.js.br +0 -0
  356. package/dist/web/assets/extension-manager-section-CTpQP6te.js.gz +0 -0
  357. package/dist/web/assets/extension-webview-COT-yLrM.js.br +0 -0
  358. package/dist/web/assets/extension-webview-COT-yLrM.js.gz +0 -0
  359. package/dist/web/assets/files-settings-section-CgVR0fqi.js.br +0 -0
  360. package/dist/web/assets/files-settings-section-CgVR0fqi.js.gz +0 -0
  361. package/dist/web/assets/general-settings-section-BEMIbE4Y.js.br +0 -0
  362. package/dist/web/assets/general-settings-section-BEMIbE4Y.js.gz +0 -0
  363. package/dist/web/assets/git-log-panel-Cp2174iI.js.br +0 -0
  364. package/dist/web/assets/git-log-panel-Cp2174iI.js.gz +0 -0
  365. package/dist/web/assets/git-review-tab-CpMJ0qDi.js.br +0 -0
  366. package/dist/web/assets/git-review-tab-CpMJ0qDi.js.gz +0 -0
  367. package/dist/web/assets/glide-data-grid-BAhwWUhO.js.br +0 -0
  368. package/dist/web/assets/glide-data-grid-BAhwWUhO.js.gz +0 -0
  369. package/dist/web/assets/grid-save-dialog-DmJhaWOl.js.br +0 -0
  370. package/dist/web/assets/grid-save-dialog-DmJhaWOl.js.gz +0 -0
  371. package/dist/web/assets/group-chat-tab-Bq7-Yegf.js.br +0 -0
  372. package/dist/web/assets/group-chat-tab-Bq7-Yegf.js.gz +0 -0
  373. package/dist/web/assets/impexp-tab-COx3Cvs7.js.br +0 -0
  374. package/dist/web/assets/impexp-tab-COx3Cvs7.js.gz +0 -0
  375. package/dist/web/assets/index-BGk8EFQb.js.br +0 -0
  376. package/dist/web/assets/index-BGk8EFQb.js.gz +0 -0
  377. package/dist/web/assets/keyboard-shortcuts-section-Bqg7AZiZ.js.br +0 -0
  378. package/dist/web/assets/keyboard-shortcuts-section-Bqg7AZiZ.js.gz +0 -0
  379. package/dist/web/assets/language-servers-section-CJ47DSIA.js.br +0 -0
  380. package/dist/web/assets/language-servers-section-CJ47DSIA.js.gz +0 -0
  381. package/dist/web/assets/logs-app-BK-n-y_K.js.br +0 -0
  382. package/dist/web/assets/logs-app-BK-n-y_K.js.gz +0 -0
  383. package/dist/web/assets/lsp-client-DHSaAyJW.js.br +0 -0
  384. package/dist/web/assets/lsp-client-DHSaAyJW.js.gz +0 -0
  385. package/dist/web/assets/markdown-renderer-CpxdNG0f.js.br +0 -0
  386. package/dist/web/assets/markdown-renderer-CpxdNG0f.js.gz +0 -0
  387. package/dist/web/assets/mobile-explorer-sheet-BBwXGmXA.js.br +0 -0
  388. package/dist/web/assets/mobile-explorer-sheet-BBwXGmXA.js.gz +0 -0
  389. package/dist/web/assets/new-design-dialog-B_ZOvGhR.js.br +0 -0
  390. package/dist/web/assets/new-design-dialog-B_ZOvGhR.js.gz +0 -0
  391. package/dist/web/assets/notifications-settings-section-D7fIVFHe.js +0 -1
  392. package/dist/web/assets/notifications-settings-section-D7fIVFHe.js.br +0 -0
  393. package/dist/web/assets/notifications-settings-section-D7fIVFHe.js.gz +0 -0
  394. package/dist/web/assets/ppmbot-settings-section-JDvyI-gZ.js.br +0 -0
  395. package/dist/web/assets/ppmbot-settings-section-JDvyI-gZ.js.gz +0 -0
  396. package/dist/web/assets/problems-panel-Bsv_L6T2.js.br +0 -0
  397. package/dist/web/assets/problems-panel-Bsv_L6T2.js.gz +0 -0
  398. package/dist/web/assets/process-table-3kZyWOJx.js.br +0 -0
  399. package/dist/web/assets/process-table-3kZyWOJx.js.gz +0 -0
  400. package/dist/web/assets/proxy-settings-section-CYcmIp7W.js.br +0 -0
  401. package/dist/web/assets/proxy-settings-section-CYcmIp7W.js.gz +0 -0
  402. package/dist/web/assets/query-tab-CjrWnQUl.js.br +0 -0
  403. package/dist/web/assets/query-tab-CjrWnQUl.js.gz +0 -0
  404. package/dist/web/assets/remote-access-settings-section-iQkakiVx.js.br +0 -0
  405. package/dist/web/assets/remote-access-settings-section-iQkakiVx.js.gz +0 -0
  406. package/dist/web/assets/remote-desktop-clipboard-notice-CFLnb7ML.js.br +0 -0
  407. package/dist/web/assets/remote-desktop-clipboard-notice-CFLnb7ML.js.gz +0 -0
  408. package/dist/web/assets/remote-desktop-mobile-sheet-C_-_-Nkz.js.br +0 -0
  409. package/dist/web/assets/remote-desktop-mobile-sheet-C_-_-Nkz.js.gz +0 -0
  410. package/dist/web/assets/remote-desktop-mobile-view-BnHSrind.js.br +0 -0
  411. package/dist/web/assets/remote-desktop-mobile-view-BnHSrind.js.gz +0 -0
  412. package/dist/web/assets/remote-desktop-readiness-gate-BGHJ7Sv6.js.br +0 -0
  413. package/dist/web/assets/remote-desktop-readiness-gate-BGHJ7Sv6.js.gz +0 -0
  414. package/dist/web/assets/remote-desktop-window-content-BBm1YwM8.js.br +0 -0
  415. package/dist/web/assets/remote-desktop-window-content-BBm1YwM8.js.gz +0 -0
  416. package/dist/web/assets/rename-object-dialog-BLnF9xyn.js.br +0 -0
  417. package/dist/web/assets/rename-object-dialog-BLnF9xyn.js.gz +0 -0
  418. package/dist/web/assets/review-code-DG7sCIzr.js.br +0 -0
  419. package/dist/web/assets/review-code-DG7sCIzr.js.gz +0 -0
  420. package/dist/web/assets/save-as-dialog-KW7cErWh.js.br +0 -0
  421. package/dist/web/assets/save-as-dialog-KW7cErWh.js.gz +0 -0
  422. package/dist/web/assets/schedules-settings-section-B4AV4ORv.js.br +0 -0
  423. package/dist/web/assets/schedules-settings-section-B4AV4ORv.js.gz +0 -0
  424. package/dist/web/assets/session-review-tab-C5nPyBzo.js.br +0 -0
  425. package/dist/web/assets/session-review-tab-C5nPyBzo.js.gz +0 -0
  426. package/dist/web/assets/settings-body-xWSmRXUD.js.br +0 -0
  427. package/dist/web/assets/settings-body-xWSmRXUD.js.gz +0 -0
  428. package/dist/web/assets/settings-rows-ChKBHk07.js.br +0 -0
  429. package/dist/web/assets/settings-rows-ChKBHk07.js.gz +0 -0
  430. package/dist/web/assets/settings-section-content-BOh2jaWV.js +0 -1
  431. package/dist/web/assets/settings-section-content-DdVdyPVX.js.br +0 -0
  432. package/dist/web/assets/settings-section-content-DdVdyPVX.js.gz +0 -0
  433. package/dist/web/assets/slash-command-picker-Cn2Fdz2p.js.br +0 -0
  434. package/dist/web/assets/slash-command-picker-Cn2Fdz2p.js.gz +0 -0
  435. package/dist/web/assets/sql-object-tab-_mPRCvD9.js.br +0 -0
  436. package/dist/web/assets/sql-object-tab-_mPRCvD9.js.gz +0 -0
  437. package/dist/web/assets/sqlite-viewer-B8VZt-mA.js.br +0 -0
  438. package/dist/web/assets/sqlite-viewer-B8VZt-mA.js.gz +0 -0
  439. package/dist/web/assets/structure-save-dialog-Lcst7kAl.js.br +0 -0
  440. package/dist/web/assets/structure-save-dialog-Lcst7kAl.js.gz +0 -0
  441. package/dist/web/assets/structure-tab-ljJCzxUe.js.br +0 -0
  442. package/dist/web/assets/structure-tab-ljJCzxUe.js.gz +0 -0
  443. package/dist/web/assets/system-monitor-body-CTi5xUZz.js.br +0 -0
  444. package/dist/web/assets/system-monitor-body-CTi5xUZz.js.gz +0 -0
  445. package/dist/web/assets/tab-host-window-content-CG0vPTc_.js.br +0 -0
  446. package/dist/web/assets/tab-host-window-content-CG0vPTc_.js.gz +0 -0
  447. package/dist/web/assets/table-tab-CTZ3nCWV.js.br +0 -0
  448. package/dist/web/assets/table-tab-CTZ3nCWV.js.gz +0 -0
  449. package/dist/web/assets/telegram-bot-token-field-DF7zq7_h.js.br +0 -0
  450. package/dist/web/assets/telegram-bot-token-field-DF7zq7_h.js.gz +0 -0
  451. package/dist/web/assets/terminal-tab-B_jECWTH.js.br +0 -0
  452. package/dist/web/assets/terminal-tab-B_jECWTH.js.gz +0 -0
  453. package/dist/web/assets/tool-cards-CsD_VXcG.js.br +0 -0
  454. package/dist/web/assets/tool-cards-CsD_VXcG.js.gz +0 -0
  455. package/dist/web/assets/tools-settings-section-DbBr0Hnr.js.br +0 -0
  456. package/dist/web/assets/tools-settings-section-DbBr0Hnr.js.gz +0 -0
  457. package/dist/web/assets/use-inline-blame-BA2mrMxT.js.br +0 -0
  458. package/dist/web/assets/use-inline-blame-BA2mrMxT.js.gz +0 -0
  459. package/dist/web/assets/use-lsp-ChbrzwvS.js.br +0 -0
  460. package/dist/web/assets/use-lsp-ChbrzwvS.js.gz +0 -0
  461. package/dist/web/assets/use-process-kill-C-YetnGd.js.br +0 -0
  462. package/dist/web/assets/use-process-kill-C-YetnGd.js.gz +0 -0
  463. package/dist/web/assets/use-sql-schema-info-pKwLD9wk.js.br +0 -0
  464. package/dist/web/assets/use-sql-schema-info-pKwLD9wk.js.gz +0 -0
  465. package/dist/web/assets/web-preview-tab-DGLPuZYM.js.br +0 -0
  466. package/dist/web/assets/web-preview-tab-DGLPuZYM.js.gz +0 -0
@@ -6,11 +6,15 @@ import { api } from "@/lib/api-client";
6
6
  import type { PushDeviceInfo } from "../../shared/notification-settings";
7
7
  import {
8
8
  PUSH_BLOCKED_MESSAGE,
9
+ PUSH_SUBSCRIBE_DEADLINE_MS,
9
10
  base64UrlToBytes,
11
+ browserNameFrom,
10
12
  bytesToBase64Url,
11
13
  deviceLabelFrom,
12
14
  isIosDevice,
15
+ pushServiceFailureMessage,
13
16
  pushSupportFrom,
17
+ withinDeadline,
14
18
  type PushSupport,
15
19
  } from "./web-push-support";
16
20
 
@@ -18,6 +22,20 @@ function ipadOs(): boolean {
18
22
  return navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
19
23
  }
20
24
 
25
+ /**
26
+ * The browser a push failure names. Brave says "Chrome" in its user agent; Vivaldi and Chromium
27
+ * do too, but list no Chrome brand, so they get "This browser" rather than a guess.
28
+ * `userAgentData` exists only in a secure context, and so does every use of this answer:
29
+ * `pushSupportFrom` stops at plain HTTP before it reads the browser.
30
+ */
31
+ function thisBrowser(): string {
32
+ if ("brave" in navigator) return "Brave";
33
+ const name = browserNameFrom(navigator.userAgent);
34
+ const brands = (navigator as Navigator & { userAgentData?: { brands: { brand: string }[] } }).userAgentData?.brands;
35
+ if (name === "Chrome" && brands && !brands.some((b) => b.brand === "Google Chrome")) return "Browser";
36
+ return name;
37
+ }
38
+
21
39
  export function pushSupport(): PushSupport {
22
40
  return pushSupportFrom({
23
41
  secure: window.isSecureContext,
@@ -28,11 +46,17 @@ export function pushSupport(): PushSupport {
28
46
  standalone: window.matchMedia?.("(display-mode: standalone)").matches
29
47
  || (navigator as Navigator & { standalone?: boolean }).standalone === true,
30
48
  permission: "Notification" in window ? Notification.permission : null,
49
+ browser: thisBrowser(),
31
50
  });
32
51
  }
33
52
 
53
+ /**
54
+ * Not `thisBrowser()`: a device list wants a name the user recognises, so a Chromium build with
55
+ * no Chrome brand (Vivaldi, Chromium) stays "Chrome" here. A push failure names the browser's
56
+ * push service, and there a wrong guess sends the user after the wrong one.
57
+ */
34
58
  export function thisDeviceLabel(): string {
35
- return deviceLabelFrom(navigator.userAgent, ipadOs());
59
+ return deviceLabelFrom(navigator.userAgent, ipadOs(), "brave" in navigator ? "Brave" : undefined);
36
60
  }
37
61
 
38
62
  /** This browser's subscription, if it has one. Never prompts. */
@@ -68,10 +92,16 @@ export async function enablePush(publicKey: string): Promise<PushDeviceInfo> {
68
92
  await subscription.unsubscribe();
69
93
  subscription = null;
70
94
  }
71
- subscription ??= await registration.pushManager.subscribe({
72
- userVisibleOnly: true,
73
- applicationServerKey: base64UrlToBytes(publicKey),
74
- });
95
+ try {
96
+ subscription ??= await withinDeadline(registration.pushManager.subscribe({
97
+ userVisibleOnly: true,
98
+ applicationServerKey: base64UrlToBytes(publicKey),
99
+ }), PUSH_SUBSCRIBE_DEADLINE_MS);
100
+ } catch (e) {
101
+ const hint = pushServiceFailureMessage(thisBrowser(), e);
102
+ if (hint) throw new Error(hint);
103
+ throw e;
104
+ }
75
105
  return api.post<PushDeviceInfo>("/api/notifications/push/subscribe", {
76
106
  subscription: subscription.toJSON(),
77
107
  label: thisDeviceLabel(),
@@ -14,6 +14,8 @@ export interface PushEnvironment {
14
14
  /** Opened from the Home Screen rather than in a browser tab. */
15
15
  standalone: boolean;
16
16
  permission: NotificationPermission | null;
17
+ /** `browserNameFrom`, or "Brave". */
18
+ browser?: string;
17
19
  }
18
20
 
19
21
  export type PushSupport =
@@ -40,27 +42,78 @@ export function pushSupportFrom(env: PushEnvironment): PushSupport {
40
42
  };
41
43
  }
42
44
  if (!env.serviceWorker || !env.pushManager || !env.notification) {
45
+ // Firefox hides PushManager, and nothing else, when `dom.push.enabled` is false.
46
+ if (env.browser === "Firefox" && env.serviceWorker && env.notification) {
47
+ return {
48
+ state: "unavailable",
49
+ message: "Push is turned off in this browser. Open about:config, set dom.push.enabled to true, then restart the browser.",
50
+ };
51
+ }
43
52
  return { state: "unavailable", message: "This browser cannot receive push notifications." };
44
53
  }
45
54
  if (env.permission === "denied") return { state: "blocked", message: PUSH_BLOCKED_MESSAGE };
46
55
  return { state: "available" };
47
56
  }
48
57
 
58
+ /**
59
+ * The first subscribe after a browser starts took ~30 s in Chrome and Brave (3 s once
60
+ * running). A push service the network blocks may never answer: with Google's hosts
61
+ * unresolvable, Chrome kept retrying for minutes and never rejected.
62
+ */
63
+ export const PUSH_SUBSCRIBE_DEADLINE_MS = 60_000;
64
+
65
+ export function withinDeadline<T>(promise: Promise<T>, ms: number): Promise<T> {
66
+ let timer: ReturnType<typeof setTimeout> | undefined;
67
+ const deadline = new Promise<never>((_, reject) => {
68
+ timer = setTimeout(() => reject(new DOMException("The push service did not answer.", "TimeoutError")), ms);
69
+ });
70
+ return Promise.race([promise, deadline]).finally(() => clearTimeout(timer));
71
+ }
72
+
73
+ /** Whose push service each browser registers with, where that is the same on every platform. */
74
+ const PUSH_SERVICE_OWNER: Record<string, string> = { Brave: "Google", Chrome: "Google", Firefox: "Mozilla", Safari: "Apple" };
75
+
76
+ /**
77
+ * What to do when `pushManager.subscribe` fails because of the push service, or null for
78
+ * any other failure. A browser reports a service it cannot use as an AbortError.
79
+ */
80
+ export function pushServiceFailureMessage(browser: string, error: unknown): string | null {
81
+ const { name, message } = (error ?? {}) as { name?: unknown; message?: unknown };
82
+ if (name !== "AbortError" && name !== "TimeoutError") return null;
83
+ // Brave turns Google's push service off by default and rejects at once.
84
+ if (browser === "Brave" && name === "AbortError") {
85
+ return "Brave keeps push off until you allow it: open brave://settings/privacy, turn on “Use Google services for push messaging”, restart Brave, then turn this on again.";
86
+ }
87
+ const owner = PUSH_SERVICE_OWNER[browser];
88
+ const service = owner ? `${owner}'s push service` : "its push service";
89
+ const failed = name === "TimeoutError" ? `got no answer from ${service} within a minute` : `could not register with ${service}`;
90
+ const said = name === "AbortError" && typeof message === "string" && message ? ` (${message})` : "";
91
+ return `${browser === "Browser" ? "This browser" : browser} ${failed}${said}. `
92
+ + `A firewall, VPN, DNS filter or ad blocker may be blocking it${owner ? "" : ", or push is turned off in this browser"}. `
93
+ + "Telegram and ntfy below work without it.";
94
+ }
95
+
49
96
  /** iPadOS reports itself as a Mac; a Mac has no touch screen. */
50
97
  export function isIosDevice(userAgent: string, platform: string, maxTouchPoints: number): boolean {
51
98
  return /iPhone|iPad|iPod/.test(userAgent) || (platform === "MacIntel" && maxTouchPoints > 1);
52
99
  }
53
100
 
54
- /** "Chrome on Android" — enough to tell a phone from a laptop in the list. */
55
- export function deviceLabelFrom(userAgent: string, ipadOs = false): string {
56
- const browser =
57
- /Edg(A|iOS)?\//.test(userAgent) ? "Edge"
101
+ /** Brave and most other Chromium builds say "Chrome"; the user agent cannot tell them apart. */
102
+ export function browserNameFrom(userAgent: string): string {
103
+ return /Edg(A|iOS)?\//.test(userAgent) ? "Edge"
58
104
  : /OPR\//.test(userAgent) ? "Opera"
59
105
  : /SamsungBrowser\//.test(userAgent) ? "Samsung Internet"
60
106
  : /Firefox\/|FxiOS\//.test(userAgent) ? "Firefox"
61
107
  : /Chrome\/|CriOS\//.test(userAgent) ? "Chrome"
62
108
  : /Safari\//.test(userAgent) ? "Safari"
63
109
  : "Browser";
110
+ }
111
+
112
+ /**
113
+ * "Chrome on Android" — enough to tell a phone from a laptop in the list. `browser` is for
114
+ * what the user agent cannot say: Brave's is Chrome's.
115
+ */
116
+ export function deviceLabelFrom(userAgent: string, ipadOs = false, browser = browserNameFrom(userAgent)): string {
64
117
  const os =
65
118
  /Android/.test(userAgent) ? "Android"
66
119
  : /iPhone|iPod/.test(userAgent) ? "iPhone"
@@ -1,3 +0,0 @@
1
- � ���r�'��i���le�X�vD.�(����r��\Σ$�b
2
- E���cX�t:�����J��I(tqY�쓽ӈT�J���Y|>��d�$Pǹ]
3
- ���Q���e�> ~�3I&�@[ �is��Mu {~Kíi�Զ�`�@Wj|�y�99�['o��$j��{O[��Wg���0dTz���l.�`Y�+�r���7�_���BW�u�ޱ�gйN�� ���B��R�)y2��?ym��LhB=?Z�rD���m2$�g/���Q/��I�X��D�[��'��k]�y�^B'8�^��2J���*�uo� ��gnV�+p3e��4��ǡش�󰑂��V�g�vq�,=IhoP�Fj�c�ǘؾX��n�a���mf��g�;�og4v�7~����Z�C-y�����h1���D�
@@ -1 +0,0 @@
1
- import{s as e}from"./rolldown-runtime-C5c2KzVm.js";import{D as t,E as n}from"./vendor-markdown-C-P1aR4p.js";import"./vendor-ui-BTSIXzhZ.js";import{Ar as r,Kr as i,Ln as a,P as o,Sr as s,gt as c,xn as l}from"./icons-BwyIKaTT.js";import{k as u,r as d}from"./api-client-xQDlhUTu.js";import{n as f}from"./utils-Bslrbb-G.js";import{t as p}from"./format-date-CSHaU2wN.js";import{n as m,t as h}from"./button-C9ZXWb3b.js";import{t as g}from"./input-C3oVNCMq.js";import{t as _}from"./separator-DmU9aJwq.js";import{n as v,r as y,t as b}from"./settings-rows-ChKBHk07.js";import{i as x,n as S,r as C,t as w}from"./telegram-bot-token-field-DF7zq7_h.js";var T=e(t(),1),E=`Notifications are blocked for this site. Allow them in the browser's site settings, then turn this on again.`;function D(e){return e.secure?e.ios&&!e.standalone?{state:`unavailable`,message:`On iPhone and iPad, push works only in PPM added to the Home Screen: tap Share → Add to Home Screen, open PPM from there, then turn this on.`}:!e.serviceWorker||!e.pushManager||!e.notification?{state:`unavailable`,message:`This browser cannot receive push notifications.`}:e.permission===`denied`?{state:`blocked`,message:E}:{state:`available`}:{state:`unavailable`,message:`Push needs PPM on HTTPS or localhost. Browsers turn it off on a plain HTTP address like this one.`}}function O(e,t,n){return/iPhone|iPad|iPod/.test(e)||t===`MacIntel`&&n>1}function k(e,t=!1){let n=/Edg(A|iOS)?\//.test(e)?`Edge`:/OPR\//.test(e)?`Opera`:/SamsungBrowser\//.test(e)?`Samsung Internet`:/Firefox\/|FxiOS\//.test(e)?`Firefox`:/Chrome\/|CriOS\//.test(e)?`Chrome`:/Safari\//.test(e)?`Safari`:`Browser`,r=/Android/.test(e)?`Android`:/iPhone|iPod/.test(e)?`iPhone`:/iPad/.test(e)||t?`iPad`:/Mac OS X/.test(e)?`macOS`:/Windows/.test(e)?`Windows`:/CrOS/.test(e)?`ChromeOS`:/Linux/.test(e)?`Linux`:``;return r?`${n} on ${r}`:n}function A(e){let t=e.replace(/-/g,`+`).replace(/_/g,`/`).padEnd(Math.ceil(e.length/4)*4,`=`),n=atob(t),r=new Uint8Array(n.length);for(let e=0;e<n.length;e++)r[e]=n.charCodeAt(e);return r}function j(e){let t=``;for(let n of e)t+=String.fromCharCode(n);return btoa(t).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function M(){return navigator.platform===`MacIntel`&&navigator.maxTouchPoints>1}function N(){return D({secure:window.isSecureContext,serviceWorker:`serviceWorker`in navigator,pushManager:`PushManager`in window,notification:`Notification`in window,ios:O(navigator.userAgent,navigator.platform,navigator.maxTouchPoints),standalone:window.matchMedia?.(`(display-mode: standalone)`).matches||navigator.standalone===!0,permission:`Notification`in window?Notification.permission:null})}function P(){return k(navigator.userAgent,M())}async function F(){return N().state===`unavailable`?null:await(await navigator.serviceWorker.getRegistration())?.pushManager.getSubscription()??null}function I(e,t){let n=e.options.applicationServerKey;return!!n&&j(new Uint8Array(n))===t}async function L(e){let t=await Notification.requestPermission();if(t!==`granted`)throw Error(t===`denied`?E:`Notifications were not allowed.`);if(!await navigator.serviceWorker.getRegistration())throw Error(`PPM's service worker is not running on this page, so it cannot receive push.`);let n=await navigator.serviceWorker.ready,r=await n.pushManager.getSubscription();return r&&!I(r,e)&&(await d.post(`/api/notifications/push/unsubscribe`,{endpoint:r.endpoint}).catch(()=>{}),await r.unsubscribe(),r=null),r??=await n.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:A(e)}),d.post(`/api/notifications/push/subscribe`,{subscription:r.toJSON(),label:P(),origin:window.location.origin})}async function R(){let e=await F();if(e)try{await d.post(`/api/notifications/push/unsubscribe`,{endpoint:e.endpoint})}finally{await e.unsubscribe()}}var z=[`done`,`approval_request`,`question`,`schedule`,`jira`,`design_review`],B=[{seconds:0,label:`Right away`},{seconds:30,label:`After 30 seconds`},{seconds:60,label:`After 1 minute`},{seconds:120,label:`After 2 minutes`},{seconds:300,label:`After 5 minutes`}],V={done:{label:`Chat finished`,note:`The AI finished answering.`},approval_request:{label:`Needs approval`,note:`A tool is waiting for your permission.`},question:{label:`AI has a question`,note:`The AI asked you something and is waiting.`},schedule:{label:`Scheduled run finished`,note:`A schedule completed or failed.`},jira:{label:`Jira`,note:`A watcher found new issues, or a debug run finished.`},design_review:{label:`Reviewer comments`,note:`Someone commented on a design you shared.`}};function H(e,t){return{events:{...e.events,...t.events??{}},delay_seconds:t.delay_seconds??e.delay_seconds,telegram:t.telegram??e.telegram,ntfy:t.ntfy??e.ntfy}}function U(e){let t=e.trim();if(!t)return null;try{let e=new URL(/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:`https://${t}`);return e.protocol!==`https:`&&e.protocol!==`http:`||!e.hostname||e.username||e.password?null:`${e.origin}${e.pathname.replace(/\/+$/,``)}`}catch{return null}}var W=n(),G=`https://ntfy.sh`;function K(){let e=crypto.getRandomValues(new Uint8Array(12));return`ppm-${Array.from(e,e=>`abcdefghijklmnopqrstuvwxyz0123456789`[e%36]).join(``)}`}function q(e){try{return new URL(e).host}catch{return e}}function J({status:e,onSaved:t,onRemove:n}){let r=(0,T.useId)(),i=(0,T.useId)(),a=(0,T.useId)(),[s,l]=(0,T.useState)(e.server||G),[u,p]=(0,T.useState)(()=>e.topic||K()),[_,v]=(0,T.useState)(``),[y,b]=(0,T.useState)(!1),[x,S]=(0,T.useState)(null),[C,w]=(0,T.useState)(!e.configured),E=U(s),D=e.tokenSet&&E===e.server,O=!e.configured||E!==e.server||u.trim()!==e.topic||_.trim()!==``,k=e=>t=>{e(t.target.value),S(null)},A=async()=>{b(!0),S(null);try{let e=await d.put(`/api/notifications/ntfy`,{server:s,topic:u,..._.trim()?{token:_.trim()}:{}});v(``),w(!1),t(e)}catch(e){S(e instanceof Error?e.message:String(e))}finally{b(!1)}};return(0,W.jsxs)(`div`,{className:`space-y-2`,children:[(0,W.jsxs)(`form`,{className:`space-y-3`,onSubmit:e=>{e.preventDefault(),A()},children:[(0,W.jsx)(Y,{id:r,label:`Server`,children:(0,W.jsx)(g,{id:r,inputMode:`url`,autoCapitalize:`none`,autoCorrect:`off`,autoComplete:`off`,spellCheck:!1,placeholder:G,value:s,onChange:k(l),className:`h-11 w-full text-sm md:h-9`})}),(0,W.jsx)(Y,{id:i,label:`Topic`,children:(0,W.jsx)(g,{id:i,autoCapitalize:`none`,autoCorrect:`off`,autoComplete:`off`,spellCheck:!1,value:u,onChange:k(p),className:`h-11 w-full font-mono text-sm md:h-9`})}),(0,W.jsx)(Y,{id:a,label:`Access token — only if the server requires sign-in`,children:(0,W.jsx)(g,{id:a,type:`password`,autoComplete:`off`,spellCheck:!1,placeholder:D?`•••••• (saved)`:`tk_…`,value:_,onChange:k(v),className:`h-11 w-full font-mono text-sm md:h-9`})}),e.tokenSet&&!D&&!_.trim()&&(0,W.jsxs)(`p`,{className:`text-xs leading-relaxed text-muted-foreground`,children:[`The saved token is for `,q(e.server),` and is not sent to another server.`]}),x&&(0,W.jsx)(`p`,{className:`text-xs text-error`,children:x}),(0,W.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row`,children:[(0,W.jsx)(h,{type:`submit`,disabled:y||!O,className:`min-h-11 cursor-pointer md:min-h-9`,children:y?`Checking…`:`Save`}),e.configured&&(0,W.jsx)(h,{type:`button`,variant:`ghost`,onClick:n,className:`min-h-11 cursor-pointer text-error hover:text-error md:min-h-9`,children:`Remove ntfy`})]})]}),(0,W.jsxs)(`button`,{type:`button`,onClick:()=>w(e=>!e),"aria-expanded":C,className:`flex min-h-11 cursor-pointer items-center gap-1 text-xs text-primary md:min-h-8`,children:[(0,W.jsx)(o,{className:f(`size-3.5 transition-transform`,!C&&`-rotate-90`)}),`How to set up ntfy`]}),C&&(0,W.jsxs)(`div`,{className:`space-y-3 rounded-lg border border-border px-4 py-3`,children:[(0,W.jsxs)(`ol`,{className:`list-decimal space-y-1.5 pl-4 text-sm leading-relaxed`,children:[(0,W.jsx)(`li`,{children:`Install the ntfy app on your phone, or open the server in a browser.`}),(0,W.jsxs)(`li`,{children:[`Subscribe to the topic above. For a server other than ntfy.sh, turn on `,(0,W.jsx)(`b`,{children:`Use another server`}),` and enter its address.`]}),(0,W.jsxs)(`li`,{children:[`If the server asks you to sign in, create a token in its web app under `,(0,W.jsx)(`b`,{children:`Account → Access tokens`}),` and paste it above. Its user needs permission to publish to the topic.`]}),(0,W.jsx)(`li`,{children:`Save, then send a test.`})]}),(0,W.jsxs)(`p`,{className:`text-xs leading-relaxed text-muted-foreground`,children:[`On ntfy.sh anyone who knows a topic's name can read it, so keep the name hard to guess. An iPhone gets instant alerts from a server of your own only when that server sets`,` `,(0,W.jsx)(`code`,{className:`rounded bg-muted px-1 py-0.5`,children:`upstream-base-url: "https://ntfy.sh"`}),`.`]}),(0,W.jsxs)(`a`,{href:`https://docs.ntfy.sh/subscribe/phone/`,target:`_blank`,rel:`noopener noreferrer`,className:f(m({variant:`outline`}),`min-h-11 w-full gap-2 md:min-h-9 sm:w-auto`),children:[`The ntfy app guide`,(0,W.jsx)(c,{className:`size-3.5`})]})]})]})}function Y({id:e,label:t,children:n}){return(0,W.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,W.jsx)(`label`,{htmlFor:e,className:`text-xs text-muted-foreground`,children:t}),n]})}var X=e=>e instanceof Error?e.message:String(e),Z=e=>p(new Date(e).toISOString());function Q(){let[e,t]=(0,T.useState)(null),[n,r]=(0,T.useState)(null),i=(0,T.useCallback)(async()=>{try{t(await d.get(`/api/notifications/settings`)),r(null)}catch(e){r(X(e))}},[]);(0,T.useEffect)(()=>{i()},[i]);let a=(0,T.useCallback)(async e=>{t(t=>t&&H(t,e));try{await d.put(`/api/notifications/settings`,e)}catch(e){u.error(`Could not save`,{description:X(e)}),await i()}},[i]);return e?(0,W.jsxs)(`div`,{className:`space-y-6`,children:[(0,W.jsx)(ee,{settings:e,update:a}),(0,W.jsx)(_,{}),(0,W.jsx)(te,{}),(0,W.jsx)(_,{}),(0,W.jsx)(ne,{settings:e,update:a}),(0,W.jsx)(_,{}),(0,W.jsx)(re,{settings:e,update:a})]}):(0,W.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:n??`Loading…`})}function ee({settings:e,update:t}){let n=(0,T.useId)(),r=B.some(t=>t.seconds===e.delay_seconds)?B:[...B,{seconds:e.delay_seconds,label:`After ${e.delay_seconds} seconds`}];return(0,W.jsxs)(`section`,{className:`space-y-3`,children:[(0,W.jsx)(v,{title:`When to notify`,children:`For every channel below, on every device.`}),(0,W.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,W.jsx)(`label`,{htmlFor:n,className:`text-xs text-muted-foreground`,children:`Send a notification`}),(0,W.jsx)(`select`,{id:n,value:e.delay_seconds,onChange:e=>void t({delay_seconds:Number(e.target.value)}),className:`h-11 w-full rounded-md border border-input bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring md:h-9`,children:r.map(e=>(0,W.jsx)(`option`,{value:e.seconds,children:e.label},e.seconds))}),(0,W.jsx)(`p`,{className:`text-xs leading-relaxed text-muted-foreground`,children:e.delay_seconds===0?`Sent the moment it happens, even while you are looking at the chat.`:`Sent only if the chat is still unread by then. Opening it in PPM on any device cancels the notification, and so does answering an approval.`})]}),(0,W.jsx)(`div`,{className:`divide-y divide-border rounded-lg border border-border`,children:z.map(n=>(0,W.jsx)(y,{label:V[n].label,note:V[n].note,checked:e.events[n],onChange:e=>void t({events:{[n]:e}})},n))})]})}function te(){let[e,t]=(0,T.useState)(null),[n,o]=(0,T.useState)(null),[c,f]=(0,T.useState)(null),[p,m]=(0,T.useState)(!1),[h,g]=(0,T.useState)(null),_=N(),x=(0,T.useCallback)(async()=>{try{let[e,n]=await Promise.all([d.get(`/api/notifications/push`),F().catch(()=>null)]);t(e),o(n),f(null)}catch(e){f(X(e))}},[]);(0,T.useEffect)(()=>{x()},[x]);let S=n&&e&&I(n,e.publicKey)?e.devices.find(e=>e.endpoint===n.endpoint)??null:null,C=async t=>{if(e){m(!0);try{t?(await L(e.publicKey),u.success(`Push is on for this device`)):await R()}catch(e){u.error(t?`Could not turn push on`:`Could not turn push off`,{description:X(e)})}finally{m(!1),await x()}}},w=async e=>{g(e.id);try{await d.post(`/api/notifications/push/test`,{id:e.id}),u.success(`Test sent to ${e.label}`)}catch(e){u.error(`Test not delivered`,{description:X(e)})}finally{g(null),await x()}},E=async e=>{if(e.id===S?.id)return C(!1);try{await d.post(`/api/notifications/push/unsubscribe`,{id:e.id})}catch(e){u.error(`Could not remove it`,{description:X(e)})}finally{await x()}},D=_.state===`unavailable`||_.state===`blocked`&&!S;return(0,W.jsxs)(`section`,{className:`space-y-3`,children:[(0,W.jsx)(v,{title:`Push notifications`,children:`Alerts on this phone or computer, even with PPM closed. Turn it on in each browser that should get them.`}),(0,W.jsx)(`div`,{className:`rounded-lg border border-border`,children:(0,W.jsx)(y,{label:`This device`,note:P(),checked:!!S,disabled:p||!e||D,onChange:e=>void C(e)})}),_.state!==`available`&&(0,W.jsx)(`p`,{className:`text-xs leading-relaxed text-warning`,children:_.message}),c&&(0,W.jsx)(`p`,{className:`text-xs text-error`,children:c}),e&&e.devices.length>0&&(0,W.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,W.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Receiving push`}),(0,W.jsx)(`ul`,{className:`divide-y divide-border rounded-lg border border-border`,children:e.devices.map(e=>(0,W.jsxs)(`li`,{className:`flex items-center gap-3 py-1.5 pl-4 pr-1`,children:[(0,W.jsx)(/Android|iPhone|iPad/.test(e.label)?r:a,{className:`size-4 shrink-0 text-muted-foreground`}),(0,W.jsxs)(`div`,{className:`min-w-0 flex-1`,children:[(0,W.jsxs)(`p`,{className:`flex min-w-0 items-center gap-1.5 text-sm`,children:[(0,W.jsx)(`span`,{className:`truncate`,children:e.label}),e.id===S?.id&&(0,W.jsx)(`span`,{className:`shrink-0 rounded bg-primary/10 px-1.5 py-0.5 text-[10px] text-primary`,children:`This device`})]}),(0,W.jsxs)(`p`,{className:`truncate text-xs text-muted-foreground`,children:[$(e.origin),` · `,e.lastSuccessAt?`delivered ${Z(e.lastSuccessAt)}`:`added ${Z(e.createdAt)}`]}),e.lastError&&(0,W.jsx)(`p`,{className:`break-words text-xs text-error`,children:e.lastError})]}),(0,W.jsx)(b,{label:`Send a test to ${e.label}`,disabled:h!==null,onClick:()=>void w(e),children:h===e.id?(0,W.jsx)(l,{className:`size-4 animate-spin`}):(0,W.jsx)(s,{className:`size-4`})}),(0,W.jsx)(b,{label:`Stop push to ${e.label}`,danger:!0,disabled:p,onClick:()=>void E(e),children:(0,W.jsx)(i,{className:`size-4`})})]},e.id))})]})]})}function $(e){try{return new URL(e).host}catch{return e}}function ne({settings:e,update:t}){let[n,r]=(0,T.useState)(null),[i,a]=(0,T.useState)(null),[o,c]=(0,T.useState)(!1),f=(0,T.useCallback)(async()=>{try{let e=await d.get(`/api/notifications/telegram`);return r(e),a(null),e}catch(e){return a(X(e)),null}},[]);(0,T.useEffect)(()=>{f()},[f]);let{link:p,starting:m,connect:g,cancel:_}=x(`/api/notifications/telegram/connect`,n,f),b=async e=>{try{await d.del(`/api/notifications/telegram/chats/${encodeURIComponent(e.chatId)}`),u.success(`Disconnected ${e.name}`,{description:`It no longer gets alerts.`})}catch(e){u.error(`Could not disconnect`,{description:X(e)})}finally{await f()}},E=async()=>{c(!0);try{await d.post(`/api/settings/telegram/test`,{}),u.success(`Test sent to Telegram`)}catch(e){u.error(`Test not delivered`,{description:X(e)})}finally{c(!1)}},D=n?.chats??[];return(0,W.jsxs)(`section`,{className:`space-y-3`,children:[(0,W.jsx)(v,{title:`Telegram`,children:`Alerts in a chat with your own Telegram bot. A chat connected here gets alerts and nothing else — PPMBot has its own bot and chats.`}),i&&(0,W.jsx)(`p`,{className:`text-xs text-error`,children:i}),n?.configured&&(0,W.jsxs)(W.Fragment,{children:[n.sharedWithPPMBot&&(0,W.jsx)(`p`,{className:`text-xs leading-relaxed text-muted-foreground`,children:`PPMBot answers through this bot too, so alerts arrive in your PPMBot chats. Give PPMBot a bot of its own in Settings → PPMBot to keep them apart.`}),(0,W.jsx)(`div`,{className:`rounded-lg border border-border`,children:(0,W.jsx)(y,{label:`Send notifications to Telegram`,note:D.length===0?`No chat connected yet`:D.length===1?`1 chat connected`:`${D.length} chats connected`,checked:e.telegram,onChange:e=>void t({telegram:e})})}),(0,W.jsx)(C,{chats:D,onDisconnect:e=>void b(e)}),p?(0,W.jsx)(S,{link:p,botUsername:n.botUsername,error:n.connect.error,onCancel:()=>void _()}):(0,W.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row`,children:[(0,W.jsxs)(h,{onClick:()=>void g(),disabled:m,className:`min-h-11 cursor-pointer gap-2 md:min-h-9`,children:[m?(0,W.jsx)(l,{className:`size-4 animate-spin`}):(0,W.jsx)(s,{className:`size-4`}),D.length===0?`Connect Telegram`:`Connect another chat`]}),D.length>0&&(0,W.jsx)(h,{variant:`outline`,onClick:()=>void E(),disabled:o,className:`min-h-11 cursor-pointer md:min-h-9`,children:o?`Sending…`:`Send a test`})]})]}),n&&(0,W.jsx)(w,{configured:n.configured,botUsername:n.botUsername,onSaved:()=>void f()})]})}function re({settings:e,update:t}){let[n,r]=(0,T.useState)(null),[i,a]=(0,T.useState)(null),[o,s]=(0,T.useState)(!1),l=(0,T.useCallback)(async()=>{try{r(await d.get(`/api/notifications/ntfy`)),a(null)}catch(e){a(X(e))}},[]);(0,T.useEffect)(()=>{l()},[l]);let p=async()=>{s(!0);try{await d.post(`/api/notifications/ntfy/test`,{}),u.success(`Test sent to ntfy`)}catch(e){u.error(`Test not delivered`,{description:X(e)})}finally{s(!1)}},g=async()=>{try{await d.del(`/api/notifications/ntfy`),u.success(`ntfy removed`)}catch(e){u.error(`Could not remove ntfy`,{description:X(e)})}finally{await l()}};return(0,W.jsxs)(`section`,{className:`space-y-3`,children:[(0,W.jsx)(v,{title:`ntfy`,children:`Alerts through an ntfy server — ntfy.sh or one of your own — on every phone and computer subscribed to your topic.`}),i&&(0,W.jsx)(`p`,{className:`text-xs text-error`,children:i}),n?.configured&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`div`,{className:`rounded-lg border border-border`,children:(0,W.jsx)(y,{label:`Send notifications to ntfy`,note:(0,W.jsxs)(W.Fragment,{children:[`Topic `,(0,W.jsx)(`span`,{className:`font-mono`,children:n.topic}),` on `,$(n.server)]}),checked:e.ntfy,onChange:e=>void t({ntfy:e})})}),(0,W.jsxs)(`div`,{className:`flex flex-col gap-2 sm:flex-row`,children:[(0,W.jsx)(h,{variant:`outline`,onClick:()=>void p(),disabled:o,className:`min-h-11 cursor-pointer md:min-h-9`,children:o?`Sending…`:`Send a test`}),(0,W.jsxs)(`a`,{href:`${n.server}/${n.topic}`,target:`_blank`,rel:`noopener noreferrer`,className:f(m({variant:`outline`}),`min-h-11 gap-2 md:min-h-9`),children:[`Open the topic`,(0,W.jsx)(c,{className:`size-3.5`})]})]})]}),n&&(0,W.jsx)(J,{status:n,onSaved:r,onRemove:()=>void g()},`${n.server}/${n.topic}`)]})}export{Q as NotificationsSettingsSection};
@@ -1 +0,0 @@
1
- import"./vendor-markdown-C-P1aR4p.js";import"./icons-BwyIKaTT.js";import{r as e}from"./settings-section-content-DdVdyPVX.js";export{e as preloadSettingsSections};