@hienlh/ppm 0.9.93 → 0.9.94

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 (218) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/web/assets/ai-settings-section-LMO_cfIW.js +1 -0
  3. package/dist/web/assets/api-client-o_6TmLGC.js +1 -0
  4. package/dist/web/assets/api-settings-CoKe_BdR.js +1 -0
  5. package/dist/web/assets/architecture-PBZL5I3N-CUZIB1Vq.js +1 -0
  6. package/dist/web/assets/arrow-up-Dtrfv490.js +1 -0
  7. package/dist/web/assets/chat-tab-DQNdrUvL.js +10 -0
  8. package/dist/web/assets/chevron-right-BzAdxJRG.js +1 -0
  9. package/dist/web/assets/code-CuravVys.js +1 -0
  10. package/dist/web/assets/code-editor-B4XNYHnl.js +8 -0
  11. package/dist/web/assets/columns-2-4fQcE4PF.js +1 -0
  12. package/dist/web/assets/conflict-editor-BcsRDSCw.js +19 -0
  13. package/dist/web/assets/createLucideIcon-BjHrJDVb.js +1 -0
  14. package/dist/web/assets/{csv-preview-BZRICDP0.js → csv-preview-BizIVMyb.js} +2 -2
  15. package/dist/web/assets/database-D4DIhgi-.js +1 -0
  16. package/dist/web/assets/database-viewer-CfzAAtm3.js +2 -0
  17. package/dist/web/assets/diff-viewer-DgA9z9Ux.js +4 -0
  18. package/dist/web/assets/dist-C5IgeqrV.js +1 -0
  19. package/dist/web/assets/dist-im4ynINo.js +11 -0
  20. package/dist/web/assets/esm-K1XIK4vc.js +2 -0
  21. package/dist/web/assets/extension-store-3yZYn07W.js +1 -0
  22. package/dist/web/assets/{extension-webview-C1d6fezE.js → extension-webview-gHGB2Nw2.js} +2 -2
  23. package/dist/web/assets/gitGraph-HDMCJU4V-CtOMUphQ.js +1 -0
  24. package/dist/web/assets/index-B4mGNywE.js +26 -0
  25. package/dist/web/assets/index-BZ4G-2BK.css +2 -0
  26. package/dist/web/assets/info-3K5VOQVL-BCrPCWGY.js +1 -0
  27. package/dist/web/assets/input-CHRMley8.js +1 -0
  28. package/dist/web/assets/keybindings-store-BAuymsWd.js +1 -0
  29. package/dist/web/assets/keybindings-store-BKyNIeFB.js +1 -0
  30. package/dist/web/assets/{lib-DSLzfeW0.js → lib-D_kRA9p6.js} +1 -1
  31. package/dist/web/assets/markdown-renderer-sIjU5LtB.js +3 -0
  32. package/dist/web/assets/packet-RMMSAZCW-D_OqB-zi.js +1 -0
  33. package/dist/web/assets/pie-UPGHQEXC-WUHpLNJz.js +1 -0
  34. package/dist/web/assets/plus-51UQ45rf.js +1 -0
  35. package/dist/web/assets/port-forwarding-tab-BMXnuRuI.js +1 -0
  36. package/dist/web/assets/postgres-viewer-B6Wj5xiN.js +3 -0
  37. package/dist/web/assets/project-store-Ciq-cK1O.js +1 -0
  38. package/dist/web/assets/radar-KQ55EAFF-HQIIecVM.js +1 -0
  39. package/dist/web/assets/react-GqWghJ-L.js +1 -0
  40. package/dist/web/assets/refresh-cw-CSFrDtiu.js +1 -0
  41. package/dist/web/assets/scroll-area-DwWF9FpN.js +1 -0
  42. package/dist/web/assets/settings-store-B470PCWf.js +2 -0
  43. package/dist/web/assets/settings-tab-BKQo79HU.js +1 -0
  44. package/dist/web/assets/{sql-query-editor-DaePHpQI.js → sql-query-editor-DZ9xskL8.js} +1 -1
  45. package/dist/web/assets/sqlite-viewer-CytNesG3.js +1 -0
  46. package/dist/web/assets/square-nsMa3iMk.js +1 -0
  47. package/dist/web/assets/tab-store-DZbiYk7y.js +1 -0
  48. package/dist/web/assets/table-Dq575bPF.js +1 -0
  49. package/dist/web/assets/terminal-tab-DjfxKMSB.js +1 -0
  50. package/dist/web/assets/text-wrap-Cn6BNQfq.js +1 -0
  51. package/dist/web/assets/trash-2-CJYoLw7Q.js +1 -0
  52. package/dist/web/assets/treemap-KZPCXAKY-0wLgUUTz.js +1 -0
  53. package/dist/web/assets/{use-monaco-theme-CM4IMROI.js → use-monaco-theme-OY18iXNi.js} +1 -1
  54. package/dist/web/assets/vendor-markdown-0Mxgxy0L.js +295 -0
  55. package/dist/web/assets/vendor-mermaid-B2SLgECS.js +2657 -0
  56. package/dist/web/assets/vendor-ui-B-T_damt.js +45 -0
  57. package/dist/web/assets/vendor-xterm-ejLe7-tK.js +36 -0
  58. package/dist/web/assets/x-DlFGzN8d.js +1 -0
  59. package/dist/web/index.html +26 -21
  60. package/dist/web/sw.js +1 -1
  61. package/docs/code-standards.md +56 -4
  62. package/docs/journals/260415-frontend-memory-optimization.md +73 -0
  63. package/docs/project-changelog.md +11 -1
  64. package/docs/system-architecture.md +36 -0
  65. package/package.json +1 -1
  66. package/src/web/components/chat/message-list.tsx +59 -22
  67. package/src/web/components/chat/tool-cards.tsx +11 -4
  68. package/src/web/components/database/data-grid.tsx +2 -1
  69. package/src/web/components/editor/code-editor.tsx +14 -8
  70. package/src/web/components/editor/conflict-editor.tsx +2 -1
  71. package/src/web/components/editor/diff-viewer.tsx +2 -1
  72. package/src/web/components/editor/editor-breadcrumb.tsx +2 -1
  73. package/src/web/components/explorer/file-tree.tsx +6 -5
  74. package/src/web/components/explorer/search-panel.tsx +2 -1
  75. package/src/web/components/git/git-status-panel.tsx +2 -1
  76. package/src/web/components/layout/add-project-form.tsx +2 -1
  77. package/src/web/components/layout/mobile-drawer.tsx +2 -1
  78. package/src/web/components/layout/mobile-nav.tsx +2 -1
  79. package/src/web/components/layout/panel-layout.tsx +3 -3
  80. package/src/web/components/layout/project-bar.tsx +7 -6
  81. package/src/web/components/layout/project-bottom-sheet.tsx +2 -1
  82. package/src/web/components/layout/sidebar.tsx +5 -4
  83. package/src/web/components/layout/status-bar.tsx +5 -4
  84. package/src/web/components/layout/tab-bar.tsx +3 -3
  85. package/src/web/components/layout/tab-content.tsx +2 -1
  86. package/src/web/components/postgres/postgres-viewer.tsx +7 -5
  87. package/src/web/components/settings/settings-tab.tsx +2 -1
  88. package/src/web/components/shared/markdown-code-block.tsx +10 -8
  89. package/src/web/components/terminal/terminal-tab.tsx +3 -3
  90. package/src/web/hooks/use-chat.ts +4 -1
  91. package/vite.config.ts +17 -0
  92. package/dist/web/assets/_basePickBy-Bj0dI1ei.js +0 -1
  93. package/dist/web/assets/_baseUniq-CyzdZeQH.js +0 -1
  94. package/dist/web/assets/ai-settings-section-Bo9lCaTd.js +0 -1
  95. package/dist/web/assets/api-client-BvxmRZUi.js +0 -1
  96. package/dist/web/assets/api-settings-CUxg9RE5.js +0 -1
  97. package/dist/web/assets/arc-CxgHJ7Z4.js +0 -1
  98. package/dist/web/assets/architecture-PBZL5I3N-DDFO_NKq.js +0 -1
  99. package/dist/web/assets/architectureDiagram-2XIMDMQ5-D16OotsC.js +0 -36
  100. package/dist/web/assets/array-BFDiaBgf.js +0 -1
  101. package/dist/web/assets/arrow-up-I9-21gkR.js +0 -1
  102. package/dist/web/assets/blockDiagram-WCTKOSBZ-Ct57Wtfk.js +0 -132
  103. package/dist/web/assets/c4Diagram-IC4MRINW-BIymcNsg.js +0 -10
  104. package/dist/web/assets/channel-wumTB1if.js +0 -1
  105. package/dist/web/assets/chat-tab-CC721_mQ.js +0 -10
  106. package/dist/web/assets/chevron-right-DY_wImxB.js +0 -1
  107. package/dist/web/assets/chunk-4BX2VUAB-CENmY7Kw.js +0 -1
  108. package/dist/web/assets/chunk-55IACEB6-DhZGI1l3.js +0 -1
  109. package/dist/web/assets/chunk-7E7YKBS2-DZcnC7Ow.js +0 -1
  110. package/dist/web/assets/chunk-7R4GIKGN-y8bfHEy-.js +0 -80
  111. package/dist/web/assets/chunk-C72U2L5F-BHPkfQj2.js +0 -1
  112. package/dist/web/assets/chunk-EGIJ26TM-nant2LXl.js +0 -1
  113. package/dist/web/assets/chunk-FMBD7UC4-Bog4cpN-.js +0 -15
  114. package/dist/web/assets/chunk-GEFDOKGD-86LFbsAC.js +0 -2
  115. package/dist/web/assets/chunk-GLR3WWYH-Re-5eSlQ.js +0 -2
  116. package/dist/web/assets/chunk-HHEYEP7N-C45i5G_3.js +0 -1
  117. package/dist/web/assets/chunk-JSJVCQXG-23eG9mgt.js +0 -1
  118. package/dist/web/assets/chunk-KX2RTZJC-CHj8TnTB.js +0 -1
  119. package/dist/web/assets/chunk-KYZI473N-gqRLpJ4w.js +0 -53
  120. package/dist/web/assets/chunk-L3YUKLVL-DnSMmNFC.js +0 -1
  121. package/dist/web/assets/chunk-MX3YWQON-B6g1ZH9X.js +0 -1
  122. package/dist/web/assets/chunk-NQ4KR5QH-DX32345Y.js +0 -220
  123. package/dist/web/assets/chunk-O4XLMI2P-Vp_V4P-b.js +0 -7
  124. package/dist/web/assets/chunk-OZEHJAEY-lKq2SWjA.js +0 -1
  125. package/dist/web/assets/chunk-PQ6SQG4A-Bik13fTV.js +0 -1
  126. package/dist/web/assets/chunk-PU5JKC2W-DD95Rx35.js +0 -70
  127. package/dist/web/assets/chunk-QZHKN3VN-N3VXx1VH.js +0 -1
  128. package/dist/web/assets/chunk-R5LLSJPH-dRhXRnrb.js +0 -1
  129. package/dist/web/assets/chunk-WL4C6EOR-B1iIvLOG.js +0 -189
  130. package/dist/web/assets/chunk-XIRO2GV7-DZBoNl1_.js +0 -1
  131. package/dist/web/assets/chunk-XPW4576I-CgLyyW03.js +0 -32
  132. package/dist/web/assets/chunk-XZSTWKYB-DjV8xl5A.js +0 -94
  133. package/dist/web/assets/chunk-YBOYWFTD-D_ILLe6_.js +0 -1
  134. package/dist/web/assets/classDiagram-VBA2DB6C-mr-Cb1me.js +0 -1
  135. package/dist/web/assets/classDiagram-v2-RAHNMMFH-BKe8_uda.js +0 -1
  136. package/dist/web/assets/clone--z5KLAuR.js +0 -1
  137. package/dist/web/assets/code-editor-BZ0xwZ4Z.js +0 -8
  138. package/dist/web/assets/columns-2-IeETSfON.js +0 -1
  139. package/dist/web/assets/conflict-editor-Bwls2-yk.js +0 -19
  140. package/dist/web/assets/cose-bilkent-S5V4N54A-BGNPFv3x.js +0 -1
  141. package/dist/web/assets/cytoscape.esm-C8i2jUzT.js +0 -321
  142. package/dist/web/assets/dagre-CkhlMHnx.js +0 -1
  143. package/dist/web/assets/dagre-KLK3FWXG-Cnp996VG.js +0 -4
  144. package/dist/web/assets/database-CgTomMxt.js +0 -1
  145. package/dist/web/assets/database-viewer-DiXWqOJH.js +0 -2
  146. package/dist/web/assets/defaultLocale-ZeknFqNe.js +0 -1
  147. package/dist/web/assets/diagram-E7M64L7V-BZF0tSOr.js +0 -24
  148. package/dist/web/assets/diagram-IFDJBPK2-nUcO8sN8.js +0 -43
  149. package/dist/web/assets/diagram-P4PSJMXO-CW0eCkwC.js +0 -24
  150. package/dist/web/assets/diff-viewer-CC-RmeV5.js +0 -4
  151. package/dist/web/assets/dist-CM0oD8tQ.js +0 -1
  152. package/dist/web/assets/dist-DZmJeHOA.js +0 -1
  153. package/dist/web/assets/erDiagram-INFDFZHY-DSkriYZ9.js +0 -70
  154. package/dist/web/assets/flowDiagram-PKNHOUZH-CFYAfZBx.js +0 -162
  155. package/dist/web/assets/ganttDiagram-A5KZAMGK-KSn4XAU4.js +0 -292
  156. package/dist/web/assets/gitGraph-HDMCJU4V-OkvBPi6H.js +0 -1
  157. package/dist/web/assets/gitGraphDiagram-K3NZZRJ6-BMgjjVys.js +0 -65
  158. package/dist/web/assets/graphlib-BWe1iK_s.js +0 -1
  159. package/dist/web/assets/index-BcIyrJiY.js +0 -26
  160. package/dist/web/assets/index-Chf0otez.css +0 -2
  161. package/dist/web/assets/info-3K5VOQVL-BDU2_bYD.js +0 -1
  162. package/dist/web/assets/infoDiagram-LFFYTUFH-Diq4Cyc3.js +0 -2
  163. package/dist/web/assets/init-0VJVrkRJ.js +0 -1
  164. package/dist/web/assets/input-BHj0veau.js +0 -45
  165. package/dist/web/assets/isArrayLikeObject-ClzWCpcm.js +0 -1
  166. package/dist/web/assets/isEmpty-BfLnxq-B.js +0 -1
  167. package/dist/web/assets/ishikawaDiagram-PHBUUO56-CiVEvp8o.js +0 -70
  168. package/dist/web/assets/journeyDiagram-4ABVD52K-CG_v5Aho.js +0 -139
  169. package/dist/web/assets/jsx-runtime-BRW_vwa9.js +0 -1
  170. package/dist/web/assets/kanban-definition-K7BYSVSG-miB0-_Zq.js +0 -89
  171. package/dist/web/assets/keybindings-store-BIufrOzJ.js +0 -1
  172. package/dist/web/assets/line-CSuSrJ9J.js +0 -1
  173. package/dist/web/assets/linear-DFN_MPsw.js +0 -1
  174. package/dist/web/assets/markdown-renderer-C5UPA1-7.js +0 -306
  175. package/dist/web/assets/math-CRc16Nj6.js +0 -1
  176. package/dist/web/assets/mermaid-parser.core-CFdP1Z5_.js +0 -4
  177. package/dist/web/assets/mindmap-definition-YRQLILUH-pYPWwASE.js +0 -68
  178. package/dist/web/assets/ordinal-DpFn432U.js +0 -1
  179. package/dist/web/assets/packet-RMMSAZCW-BwpIpYB3.js +0 -1
  180. package/dist/web/assets/path-INs8XTPH.js +0 -1
  181. package/dist/web/assets/pie-UPGHQEXC-BPgAfmes.js +0 -1
  182. package/dist/web/assets/pieDiagram-SKSYHLDU-Dovdlvhu.js +0 -30
  183. package/dist/web/assets/plus-DQGIb4mQ.js +0 -1
  184. package/dist/web/assets/port-forwarding-tab-DmifthYH.js +0 -1
  185. package/dist/web/assets/postgres-viewer-Bo7jEQfQ.js +0 -13
  186. package/dist/web/assets/preload-helper-mr3rCizq.js +0 -1
  187. package/dist/web/assets/quadrantDiagram-337W2JSQ-TXe6cU_F.js +0 -7
  188. package/dist/web/assets/radar-KQ55EAFF-TqxBkWx-.js +0 -1
  189. package/dist/web/assets/react-0tkk-ztn.js +0 -1
  190. package/dist/web/assets/react-dom-Bpkvzu3U.js +0 -1
  191. package/dist/web/assets/react-nm2Ru1Pt.js +0 -1
  192. package/dist/web/assets/refresh-cw-Clk8fdUD.js +0 -1
  193. package/dist/web/assets/requirementDiagram-Z7DCOOCP-CuiiuGS9.js +0 -73
  194. package/dist/web/assets/rough.esm-eLccZ4OJ.js +0 -1
  195. package/dist/web/assets/sankeyDiagram-WA2Y5GQK-BbRmhv0t.js +0 -10
  196. package/dist/web/assets/scroll-area-BpXCNme3.js +0 -1
  197. package/dist/web/assets/sequenceDiagram-2WXFIKYE-B2D8IQDb.js +0 -145
  198. package/dist/web/assets/settings-tab-D9GicyA9.js +0 -1
  199. package/dist/web/assets/sqlite-viewer-pacZlViY.js +0 -1
  200. package/dist/web/assets/square-vBdqj0bF.js +0 -1
  201. package/dist/web/assets/src-CqyWLlNZ.js +0 -1
  202. package/dist/web/assets/stateDiagram-RAJIS63D-ylr4HxPu.js +0 -1
  203. package/dist/web/assets/stateDiagram-v2-FVOUBMTO-D6zvxf3M.js +0 -1
  204. package/dist/web/assets/table-Bi27fEaN.js +0 -1
  205. package/dist/web/assets/terminal-tab-DpzE3yoD.js +0 -36
  206. package/dist/web/assets/text-wrap-D_OmSzhp.js +0 -1
  207. package/dist/web/assets/timeline-definition-YZTLITO2-pMv1grvM.js +0 -61
  208. package/dist/web/assets/trash-2-CNuB-htI.js +0 -1
  209. package/dist/web/assets/treemap-KZPCXAKY-Kck06FKU.js +0 -1
  210. package/dist/web/assets/vennDiagram-LZ73GAT5-C-rkIUbo.js +0 -34
  211. package/dist/web/assets/x-Dw3TjeY_.js +0 -1
  212. package/dist/web/assets/xychartDiagram-JWTSCODW-CtpjAakO.js +0 -7
  213. /package/dist/web/assets/{csv-parser-i7fjqP2H.js → csv-parser--2WJNgS7.js} +0 -0
  214. /package/dist/web/assets/{katex-DR0kdMDv.js → katex-CKoArbIw.js} +0 -0
  215. /package/dist/web/assets/{chunk-CFjPhJqf.js → rolldown-runtime-FhOqtrmT.js} +0 -0
  216. /package/dist/web/assets/{sql-completion-provider-B8uUWWej.js → sql-completion-provider-C3cq9j99.js} +0 -0
  217. /package/dist/web/assets/{utils-DX8jb5qv.js → utils-ChWX7pZv.js} +0 -0
  218. /package/dist/web/assets/{terminal-tab-BrP-ENHg.css → vendor-xterm-BrP-ENHg.css} +0 -0
@@ -0,0 +1 @@
1
+ import{t as e}from"./createLucideIcon-BjHrJDVb.js";var t=e(`download`,[[`path`,{d:`M12 15V3`,key:`m9g1x1`}],[`path`,{d:`M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4`,key:`ih7n3h`}],[`path`,{d:`m7 10 5 5 5-5`,key:`brsn70`}]]),n=e(`eye`,[[`path`,{d:`M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0`,key:`1nclc0`}],[`circle`,{cx:`12`,cy:`12`,r:`3`,key:`1v7zrd`}]]),r=e(`x`,[[`path`,{d:`M18 6 6 18`,key:`1bl5f8`}],[`path`,{d:`m6 6 12 12`,key:`d8bk6v`}]]);export{n,t as r,r as t};
@@ -39,27 +39,32 @@
39
39
  <link rel="preconnect" href="https://fonts.googleapis.com" />
40
40
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
41
41
  <link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&family=Geist:wght@400;500;600;700&display=swap" rel="stylesheet" />
42
- <script type="module" crossorigin src="/assets/index-BcIyrJiY.js"></script>
43
- <link rel="modulepreload" crossorigin href="/assets/chunk-CFjPhJqf.js">
44
- <link rel="modulepreload" crossorigin href="/assets/preload-helper-mr3rCizq.js">
45
- <link rel="modulepreload" crossorigin href="/assets/utils-DX8jb5qv.js">
46
- <link rel="modulepreload" crossorigin href="/assets/react-nm2Ru1Pt.js">
47
- <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-BRW_vwa9.js">
48
- <link rel="modulepreload" crossorigin href="/assets/x-Dw3TjeY_.js">
49
- <link rel="modulepreload" crossorigin href="/assets/react-dom-Bpkvzu3U.js">
50
- <link rel="modulepreload" crossorigin href="/assets/input-BHj0veau.js">
51
- <link rel="modulepreload" crossorigin href="/assets/dist-CM0oD8tQ.js">
52
- <link rel="modulepreload" crossorigin href="/assets/plus-DQGIb4mQ.js">
53
- <link rel="modulepreload" crossorigin href="/assets/refresh-cw-Clk8fdUD.js">
54
- <link rel="modulepreload" crossorigin href="/assets/trash-2-CNuB-htI.js">
55
- <link rel="modulepreload" crossorigin href="/assets/api-client-BvxmRZUi.js">
56
- <link rel="modulepreload" crossorigin href="/assets/api-settings-CUxg9RE5.js">
57
- <link rel="modulepreload" crossorigin href="/assets/ai-settings-section-Bo9lCaTd.js">
58
- <link rel="modulepreload" crossorigin href="/assets/scroll-area-BpXCNme3.js">
59
- <link rel="modulepreload" crossorigin href="/assets/chevron-right-DY_wImxB.js">
60
- <link rel="modulepreload" crossorigin href="/assets/database-CgTomMxt.js">
61
- <link rel="modulepreload" crossorigin href="/assets/react-0tkk-ztn.js">
62
- <link rel="stylesheet" crossorigin href="/assets/index-Chf0otez.css">
42
+ <script type="module" crossorigin src="/assets/index-B4mGNywE.js"></script>
43
+ <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-FhOqtrmT.js">
44
+ <link rel="modulepreload" crossorigin href="/assets/vendor-mermaid-B2SLgECS.js">
45
+ <link rel="modulepreload" crossorigin href="/assets/vendor-markdown-0Mxgxy0L.js">
46
+ <link rel="modulepreload" crossorigin href="/assets/vendor-ui-B-T_damt.js">
47
+ <link rel="modulepreload" crossorigin href="/assets/utils-ChWX7pZv.js">
48
+ <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-BjHrJDVb.js">
49
+ <link rel="modulepreload" crossorigin href="/assets/x-DlFGzN8d.js">
50
+ <link rel="modulepreload" crossorigin href="/assets/input-CHRMley8.js">
51
+ <link rel="modulepreload" crossorigin href="/assets/scroll-area-DwWF9FpN.js">
52
+ <link rel="modulepreload" crossorigin href="/assets/dist-C5IgeqrV.js">
53
+ <link rel="modulepreload" crossorigin href="/assets/plus-51UQ45rf.js">
54
+ <link rel="modulepreload" crossorigin href="/assets/refresh-cw-CSFrDtiu.js">
55
+ <link rel="modulepreload" crossorigin href="/assets/trash-2-CJYoLw7Q.js">
56
+ <link rel="modulepreload" crossorigin href="/assets/api-client-o_6TmLGC.js">
57
+ <link rel="modulepreload" crossorigin href="/assets/api-settings-CoKe_BdR.js">
58
+ <link rel="modulepreload" crossorigin href="/assets/ai-settings-section-LMO_cfIW.js">
59
+ <link rel="modulepreload" crossorigin href="/assets/chevron-right-BzAdxJRG.js">
60
+ <link rel="modulepreload" crossorigin href="/assets/database-D4DIhgi-.js">
61
+ <link rel="modulepreload" crossorigin href="/assets/react-GqWghJ-L.js">
62
+ <link rel="modulepreload" crossorigin href="/assets/extension-store-3yZYn07W.js">
63
+ <link rel="modulepreload" crossorigin href="/assets/keybindings-store-BAuymsWd.js">
64
+ <link rel="modulepreload" crossorigin href="/assets/tab-store-DZbiYk7y.js">
65
+ <link rel="modulepreload" crossorigin href="/assets/project-store-Ciq-cK1O.js">
66
+ <link rel="modulepreload" crossorigin href="/assets/settings-store-B470PCWf.js">
67
+ <link rel="stylesheet" crossorigin href="/assets/index-BZ4G-2BK.css">
63
68
  <link rel="manifest" href="/manifest.webmanifest"><script id="vite-plugin-pwa:register-sw" src="/registerSW.js"></script></head>
64
69
  <body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
65
70
  <div id="root"></div>
package/dist/web/sw.js CHANGED
@@ -1 +1 @@
1
- try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"2acfba999ac6948556cd14fc22ee7ac9","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/xychartDiagram-JWTSCODW-CtpjAakO.js"},{"revision":null,"url":"assets/x-Dw3TjeY_.js"},{"revision":null,"url":"assets/vennDiagram-LZ73GAT5-C-rkIUbo.js"},{"revision":null,"url":"assets/utils-DX8jb5qv.js"},{"revision":null,"url":"assets/use-monaco-theme-CM4IMROI.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-Kck06FKU.js"},{"revision":null,"url":"assets/trash-2-CNuB-htI.js"},{"revision":null,"url":"assets/timeline-definition-YZTLITO2-pMv1grvM.js"},{"revision":null,"url":"assets/text-wrap-D_OmSzhp.js"},{"revision":null,"url":"assets/terminal-tab-DpzE3yoD.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/table-Bi27fEaN.js"},{"revision":null,"url":"assets/stateDiagram-v2-FVOUBMTO-D6zvxf3M.js"},{"revision":null,"url":"assets/stateDiagram-RAJIS63D-ylr4HxPu.js"},{"revision":null,"url":"assets/src-CqyWLlNZ.js"},{"revision":null,"url":"assets/square-vBdqj0bF.js"},{"revision":null,"url":"assets/sqlite-viewer-pacZlViY.js"},{"revision":null,"url":"assets/sql-query-editor-DaePHpQI.js"},{"revision":null,"url":"assets/sql-completion-provider-B8uUWWej.js"},{"revision":null,"url":"assets/settings-tab-D9GicyA9.js"},{"revision":null,"url":"assets/sequenceDiagram-2WXFIKYE-B2D8IQDb.js"},{"revision":null,"url":"assets/scroll-area-BpXCNme3.js"},{"revision":null,"url":"assets/sankeyDiagram-WA2Y5GQK-BbRmhv0t.js"},{"revision":null,"url":"assets/rough.esm-eLccZ4OJ.js"},{"revision":null,"url":"assets/requirementDiagram-Z7DCOOCP-CuiiuGS9.js"},{"revision":null,"url":"assets/refresh-cw-Clk8fdUD.js"},{"revision":null,"url":"assets/react-nm2Ru1Pt.js"},{"revision":null,"url":"assets/react-dom-Bpkvzu3U.js"},{"revision":null,"url":"assets/react-0tkk-ztn.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-TqxBkWx-.js"},{"revision":null,"url":"assets/quadrantDiagram-337W2JSQ-TXe6cU_F.js"},{"revision":null,"url":"assets/preload-helper-mr3rCizq.js"},{"revision":null,"url":"assets/postgres-viewer-Bo7jEQfQ.js"},{"revision":null,"url":"assets/port-forwarding-tab-DmifthYH.js"},{"revision":null,"url":"assets/plus-DQGIb4mQ.js"},{"revision":null,"url":"assets/pieDiagram-SKSYHLDU-Dovdlvhu.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-BPgAfmes.js"},{"revision":null,"url":"assets/path-INs8XTPH.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-BwpIpYB3.js"},{"revision":null,"url":"assets/ordinal-DpFn432U.js"},{"revision":null,"url":"assets/mindmap-definition-YRQLILUH-pYPWwASE.js"},{"revision":null,"url":"assets/mermaid-parser.core-CFdP1Z5_.js"},{"revision":null,"url":"assets/math-CRc16Nj6.js"},{"revision":null,"url":"assets/markdown-renderer-C5UPA1-7.js"},{"revision":null,"url":"assets/linear-DFN_MPsw.js"},{"revision":null,"url":"assets/line-CSuSrJ9J.js"},{"revision":null,"url":"assets/lib-DSLzfeW0.js"},{"revision":null,"url":"assets/keybindings-store-BIufrOzJ.js"},{"revision":null,"url":"assets/katex-DR0kdMDv.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-miB0-_Zq.js"},{"revision":null,"url":"assets/jsx-runtime-BRW_vwa9.js"},{"revision":null,"url":"assets/journeyDiagram-4ABVD52K-CG_v5Aho.js"},{"revision":null,"url":"assets/ishikawaDiagram-PHBUUO56-CiVEvp8o.js"},{"revision":null,"url":"assets/isEmpty-BfLnxq-B.js"},{"revision":null,"url":"assets/isArrayLikeObject-ClzWCpcm.js"},{"revision":null,"url":"assets/input-BHj0veau.js"},{"revision":null,"url":"assets/init-0VJVrkRJ.js"},{"revision":null,"url":"assets/infoDiagram-LFFYTUFH-Diq4Cyc3.js"},{"revision":null,"url":"assets/info-3K5VOQVL-BDU2_bYD.js"},{"revision":null,"url":"assets/index-Chf0otez.css"},{"revision":null,"url":"assets/index-BcIyrJiY.js"},{"revision":null,"url":"assets/graphlib-BWe1iK_s.js"},{"revision":null,"url":"assets/gitGraphDiagram-K3NZZRJ6-BMgjjVys.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-OkvBPi6H.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-KSn4XAU4.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CFYAfZBx.js"},{"revision":null,"url":"assets/extension-webview-C1d6fezE.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-DSkriYZ9.js"},{"revision":null,"url":"assets/dist-DZmJeHOA.js"},{"revision":null,"url":"assets/dist-CM0oD8tQ.js"},{"revision":null,"url":"assets/diff-viewer-CC-RmeV5.js"},{"revision":null,"url":"assets/diagram-P4PSJMXO-CW0eCkwC.js"},{"revision":null,"url":"assets/diagram-IFDJBPK2-nUcO8sN8.js"},{"revision":null,"url":"assets/diagram-E7M64L7V-BZF0tSOr.js"},{"revision":null,"url":"assets/defaultLocale-ZeknFqNe.js"},{"revision":null,"url":"assets/database-viewer-DiXWqOJH.js"},{"revision":null,"url":"assets/database-CgTomMxt.js"},{"revision":null,"url":"assets/dagre-KLK3FWXG-Cnp996VG.js"},{"revision":null,"url":"assets/dagre-CkhlMHnx.js"},{"revision":null,"url":"assets/cytoscape.esm-C8i2jUzT.js"},{"revision":null,"url":"assets/csv-preview-BZRICDP0.js"},{"revision":null,"url":"assets/csv-parser-i7fjqP2H.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-BGNPFv3x.js"},{"revision":null,"url":"assets/conflict-editor-Bwls2-yk.js"},{"revision":null,"url":"assets/columns-2-IeETSfON.js"},{"revision":null,"url":"assets/code-editor-BZ0xwZ4Z.js"},{"revision":null,"url":"assets/clone--z5KLAuR.js"},{"revision":null,"url":"assets/classDiagram-v2-RAHNMMFH-BKe8_uda.js"},{"revision":null,"url":"assets/classDiagram-VBA2DB6C-mr-Cb1me.js"},{"revision":null,"url":"assets/chunk-YBOYWFTD-D_ILLe6_.js"},{"revision":null,"url":"assets/chunk-XZSTWKYB-DjV8xl5A.js"},{"revision":null,"url":"assets/chunk-XPW4576I-CgLyyW03.js"},{"revision":null,"url":"assets/chunk-XIRO2GV7-DZBoNl1_.js"},{"revision":null,"url":"assets/chunk-WL4C6EOR-B1iIvLOG.js"},{"revision":null,"url":"assets/chunk-R5LLSJPH-dRhXRnrb.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-N3VXx1VH.js"},{"revision":null,"url":"assets/chunk-PU5JKC2W-DD95Rx35.js"},{"revision":null,"url":"assets/chunk-PQ6SQG4A-Bik13fTV.js"},{"revision":null,"url":"assets/chunk-OZEHJAEY-lKq2SWjA.js"},{"revision":null,"url":"assets/chunk-O4XLMI2P-Vp_V4P-b.js"},{"revision":null,"url":"assets/chunk-NQ4KR5QH-DX32345Y.js"},{"revision":null,"url":"assets/chunk-MX3YWQON-B6g1ZH9X.js"},{"revision":null,"url":"assets/chunk-L3YUKLVL-DnSMmNFC.js"},{"revision":null,"url":"assets/chunk-KYZI473N-gqRLpJ4w.js"},{"revision":null,"url":"assets/chunk-KX2RTZJC-CHj8TnTB.js"},{"revision":null,"url":"assets/chunk-JSJVCQXG-23eG9mgt.js"},{"revision":null,"url":"assets/chunk-HHEYEP7N-C45i5G_3.js"},{"revision":null,"url":"assets/chunk-GLR3WWYH-Re-5eSlQ.js"},{"revision":null,"url":"assets/chunk-GEFDOKGD-86LFbsAC.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-Bog4cpN-.js"},{"revision":null,"url":"assets/chunk-EGIJ26TM-nant2LXl.js"},{"revision":null,"url":"assets/chunk-CFjPhJqf.js"},{"revision":null,"url":"assets/chunk-C72U2L5F-BHPkfQj2.js"},{"revision":null,"url":"assets/chunk-7R4GIKGN-y8bfHEy-.js"},{"revision":null,"url":"assets/chunk-7E7YKBS2-DZcnC7Ow.js"},{"revision":null,"url":"assets/chunk-55IACEB6-DhZGI1l3.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-CENmY7Kw.js"},{"revision":null,"url":"assets/chevron-right-DY_wImxB.js"},{"revision":null,"url":"assets/chat-tab-CC721_mQ.js"},{"revision":null,"url":"assets/channel-wumTB1if.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW-BIymcNsg.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-Ct57Wtfk.js"},{"revision":null,"url":"assets/arrow-up-I9-21gkR.js"},{"revision":null,"url":"assets/array-BFDiaBgf.js"},{"revision":null,"url":"assets/architectureDiagram-2XIMDMQ5-D16OotsC.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-DDFO_NKq.js"},{"revision":null,"url":"assets/arc-CxgHJ7Z4.js"},{"revision":null,"url":"assets/api-settings-CUxg9RE5.js"},{"revision":null,"url":"assets/api-client-BvxmRZUi.js"},{"revision":null,"url":"assets/ai-settings-section-Bo9lCaTd.js"},{"revision":null,"url":"assets/_baseUniq-CyzdZeQH.js"},{"revision":null,"url":"assets/_basePickBy-Bj0dI1ei.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
1
+ try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||=(self.addEventListener(`install`,this.install),self.addEventListener(`activate`,this.activate),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o==`boolean`)&&(i=void 0),{route:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"bb17db0c4e0bcb9022212d56deaeeeaf","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/x-DlFGzN8d.js"},{"revision":null,"url":"assets/vendor-xterm-ejLe7-tK.js"},{"revision":null,"url":"assets/vendor-xterm-BrP-ENHg.css"},{"revision":null,"url":"assets/vendor-ui-B-T_damt.js"},{"revision":null,"url":"assets/vendor-mermaid-B2SLgECS.js"},{"revision":null,"url":"assets/vendor-markdown-0Mxgxy0L.js"},{"revision":null,"url":"assets/utils-ChWX7pZv.js"},{"revision":null,"url":"assets/use-monaco-theme-OY18iXNi.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-0wLgUUTz.js"},{"revision":null,"url":"assets/trash-2-CJYoLw7Q.js"},{"revision":null,"url":"assets/text-wrap-Cn6BNQfq.js"},{"revision":null,"url":"assets/terminal-tab-DjfxKMSB.js"},{"revision":null,"url":"assets/table-Dq575bPF.js"},{"revision":null,"url":"assets/tab-store-DZbiYk7y.js"},{"revision":null,"url":"assets/square-nsMa3iMk.js"},{"revision":null,"url":"assets/sqlite-viewer-CytNesG3.js"},{"revision":null,"url":"assets/sql-query-editor-DZ9xskL8.js"},{"revision":null,"url":"assets/sql-completion-provider-C3cq9j99.js"},{"revision":null,"url":"assets/settings-tab-BKQo79HU.js"},{"revision":null,"url":"assets/settings-store-B470PCWf.js"},{"revision":null,"url":"assets/scroll-area-DwWF9FpN.js"},{"revision":null,"url":"assets/rolldown-runtime-FhOqtrmT.js"},{"revision":null,"url":"assets/refresh-cw-CSFrDtiu.js"},{"revision":null,"url":"assets/react-GqWghJ-L.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-HQIIecVM.js"},{"revision":null,"url":"assets/project-store-Ciq-cK1O.js"},{"revision":null,"url":"assets/postgres-viewer-B6Wj5xiN.js"},{"revision":null,"url":"assets/port-forwarding-tab-BMXnuRuI.js"},{"revision":null,"url":"assets/plus-51UQ45rf.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-WUHpLNJz.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-D_OqB-zi.js"},{"revision":null,"url":"assets/markdown-renderer-sIjU5LtB.js"},{"revision":null,"url":"assets/lib-D_kRA9p6.js"},{"revision":null,"url":"assets/keybindings-store-BKyNIeFB.js"},{"revision":null,"url":"assets/keybindings-store-BAuymsWd.js"},{"revision":null,"url":"assets/katex-CKoArbIw.js"},{"revision":null,"url":"assets/input-CHRMley8.js"},{"revision":null,"url":"assets/info-3K5VOQVL-BCrPCWGY.js"},{"revision":null,"url":"assets/index-BZ4G-2BK.css"},{"revision":null,"url":"assets/index-B4mGNywE.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-CtOMUphQ.js"},{"revision":null,"url":"assets/extension-webview-gHGB2Nw2.js"},{"revision":null,"url":"assets/extension-store-3yZYn07W.js"},{"revision":null,"url":"assets/esm-K1XIK4vc.js"},{"revision":null,"url":"assets/dist-im4ynINo.js"},{"revision":null,"url":"assets/dist-C5IgeqrV.js"},{"revision":null,"url":"assets/diff-viewer-DgA9z9Ux.js"},{"revision":null,"url":"assets/database-viewer-CfzAAtm3.js"},{"revision":null,"url":"assets/database-D4DIhgi-.js"},{"revision":null,"url":"assets/csv-preview-BizIVMyb.js"},{"revision":null,"url":"assets/csv-parser--2WJNgS7.js"},{"revision":null,"url":"assets/createLucideIcon-BjHrJDVb.js"},{"revision":null,"url":"assets/conflict-editor-BcsRDSCw.js"},{"revision":null,"url":"assets/columns-2-4fQcE4PF.js"},{"revision":null,"url":"assets/code-editor-B4XNYHnl.js"},{"revision":null,"url":"assets/code-CuravVys.js"},{"revision":null,"url":"assets/chevron-right-BzAdxJRG.js"},{"revision":null,"url":"assets/chat-tab-DQNdrUvL.js"},{"revision":null,"url":"assets/arrow-up-Dtrfv490.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-CUZIB1Vq.js"},{"revision":null,"url":"assets/api-settings-CoKe_BdR.js"},{"revision":null,"url":"assets/api-client-o_6TmLGC.js"},{"revision":null,"url":"assets/ai-settings-section-LMO_cfIW.js"},{"revision":null,"url":"assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2"},{"revision":null,"url":"assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2"},{"revision":null,"url":"assets/KaTeX_Size2-Regular-Dy4dx90m.woff2"},{"revision":null,"url":"assets/KaTeX_Size1-Regular-mCD8mA8B.woff2"},{"revision":null,"url":"assets/KaTeX_Script-Regular-D3wIWfF6.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2"},{"revision":null,"url":"assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2"},{"revision":null,"url":"assets/KaTeX_Math-Italic-t53AETM-.woff2"},{"revision":null,"url":"assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2"},{"revision":null,"url":"assets/KaTeX_Main-Regular-B22Nviop.woff2"},{"revision":null,"url":"assets/KaTeX_Main-Italic-NWA7e6Wa.woff2"},{"revision":null,"url":"assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2"},{"revision":null,"url":"assets/KaTeX_Main-Bold-Cx986IdX.woff2"},{"revision":null,"url":"assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2"},{"revision":null,"url":"assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2"},{"revision":null,"url":"assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2"},{"revision":null,"url":"assets/KaTeX_AMS-Regular-BQhdFMY1.woff2"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
@@ -146,8 +146,8 @@ export function ChatTab() {
146
146
  }
147
147
  ```
148
148
 
149
- ### Zustand Stores
150
- Define stores as singleton exports. Use selectors to subscribe to specific state:
149
+ ### Zustand Stores (useShallow)
150
+ Define stores as singleton exports. Use `useShallow` when destructuring state to prevent unnecessary re-renders:
151
151
 
152
152
  ```typescript
153
153
  // Good: src/web/stores/chat-store.ts
@@ -156,10 +156,28 @@ export const chatStore = create<ChatState>((set) => ({
156
156
  addMessage: (msg) => set((state) => ({ messages: [...state.messages, msg] })),
157
157
  }));
158
158
 
159
- // Usage with selector (avoids full re-render)
159
+ // Usage with useShallow (avoids full re-render on object mutations)
160
+ const { messages, addMessage } = chatStore(useShallow((state) => ({
161
+ messages: state.messages,
162
+ addMessage: state.addMessage,
163
+ })));
164
+
165
+ // Single selector still works without useShallow
160
166
  const messages = chatStore((state) => state.messages);
161
167
  ```
162
168
 
169
+ ### Component Memoization (React.memo)
170
+ Memoize expensive components to prevent re-renders from parent updates:
171
+
172
+ ```typescript
173
+ // Good: Memoized heavy component
174
+ export const CodeEditor = memo(function CodeEditor({ filePath }: Props) {
175
+ return <MonacoEditor path={filePath} />;
176
+ });
177
+
178
+ // Memoized components: CodeEditor, MessageBubble, ProjectBar, ProjectAvatar, TerminalTab, PanelLayout, Sidebar, StatusBar, StatusBarEntry, TabBar, TreeNode
179
+ ```
180
+
163
181
  ### Custom Hooks
164
182
  Extract logic into hooks for reusability. Return stable references:
165
183
 
@@ -616,13 +634,47 @@ type FileStatus =
616
634
  ## Performance Conventions
617
635
 
618
636
  ### Code Splitting
619
- Use React.lazy() for routes and heavy components:
637
+ Use `React.lazy()` + `Suspense` for routes, tab components, and heavy renderers:
620
638
 
621
639
  ```typescript
622
640
  // Good: Lazy-load terminal component
623
641
  const TerminalTab = lazy(() => import("./terminal-tab"));
642
+
643
+ // Good: Lazy-load markdown renderer on 3+ sites
644
+ const MarkdownRenderer = lazy(() => import("./markdown-renderer"));
645
+ ```
646
+
647
+ ### Vendor Chunk Splitting (vite.config.ts)
648
+ Manually split large vendor libraries into separate chunks for better caching:
649
+
650
+ ```typescript
651
+ // Chunks: vendor-monaco, vendor-mermaid, vendor-xterm, vendor-markdown, vendor-ui
652
+ manualChunks(id: string) {
653
+ if (id.includes("node_modules/monaco-editor")) return "vendor-monaco";
654
+ if (id.includes("node_modules/mermaid")) return "vendor-mermaid";
655
+ if (id.includes("node_modules/@xterm")) return "vendor-xterm";
656
+ // ... etc
657
+ }
658
+ ```
659
+
660
+ ### Dynamic Imports (On-Demand Loading)
661
+ Load heavy libraries only when needed:
662
+
663
+ ```typescript
664
+ // Good: Mermaid diagram support on-demand in markdown
665
+ let mermaidPromise: Promise<typeof import("mermaid")> | null = null;
666
+ async function loadMermaid() {
667
+ if (!mermaidPromise) {
668
+ mermaidPromise = import("mermaid").then((mod) => mod.default);
669
+ }
670
+ return mermaidPromise;
671
+ }
624
672
  ```
625
673
 
674
+ ### Chat Pagination & Message Caps
675
+ - **Chat history:** Load 50 messages per page with load-more button
676
+ - **Team activity:** Cap `teamActivityRef` at 500 messages to prevent unbounded growth
677
+
626
678
  ### Memoization
627
679
  Memoize expensive computations and callbacks:
628
680
 
@@ -0,0 +1,73 @@
1
+ # Frontend Memory Optimization: Four Phases of Rendering & Bundle Reduction
2
+
3
+ **Date**: 2026-04-15 18:00
4
+ **Severity**: Medium
5
+ **Component**: Frontend (React, Vite bundles)
6
+ **Status**: Resolved (with incident)
7
+
8
+ ## What Happened
9
+
10
+ Completed 4-phase memory and rendering optimization across the React codebase. Added shallow equality checks, memoization, lazy loading, and code splitting to reduce initial bundle size and re-render overhead. Process was interrupted by a critical git incident mid-session.
11
+
12
+ ## The Brutal Truth
13
+
14
+ This was simultaneously a win and a disaster. The optimization work itself is solid—17 files now use `useShallow` to prevent unnecessary re-renders, 11 heavy components wrapped in `React.memo`, and critical libraries (mermaid, markdown, xterm) are now lazy-loaded. But a subagent ran `git reset --hard HEAD` without warning, destroying ALL uncommitted changes. Had to re-implement everything from scratch. Incredibly frustrating because the technical work was already done—it just got erased.
15
+
16
+ ## Technical Details
17
+
18
+ **Phase 1 (Shallow Equality)**
19
+ - Added `useShallow` to 17 files with destructured store selector calls
20
+ - Prevents re-renders when store object reference changes but content doesn't
21
+ - Affected: hooks, chat, sidebar, tree, editor, terminal components
22
+
23
+ **Phase 2 (Chat Pagination)**
24
+ - PAGE_SIZE=50 pagination with load-more button in message-list.tsx
25
+ - Capped teamActivityRef at 500 messages in use-chat.ts
26
+ - Prevents DOM from holding thousands of message elements
27
+
28
+ **Phase 3 (Lazy Loading)**
29
+ - React.lazy for MarkdownRenderer (3 call sites, 175KB bundle)
30
+ - Dynamic import for mermaid in markdown-code-block.tsx with promise caching
31
+ - React.lazy for CodeMirror in postgres-viewer.tsx
32
+ - Fallback suspense boundaries in place
33
+
34
+ **Phase 4 (Code Splitting)**
35
+ - Vite manualChunks: vendor-monaco (lazy), vendor-mermaid (2.4MB lazy), vendor-xterm (344KB lazy), vendor-markdown (597KB lazy), vendor-ui (128KB shared)
36
+ - React.memo on TreeNode
37
+ - Initial bundle reduced to 495KB
38
+
39
+ ## Code Review Fixes Applied
40
+
41
+ 1. Rules of Hooks violation in MessageList—moved hooks above early returns
42
+ 2. Pagination reset on every streaming tick—fixed deps to stable `messages[0]?.id`
43
+ 3. onFork inline closure defeating MessageBubble memo—made stable with useCallback
44
+ 4. Variable shadowing (`s` in tool-cards.tsx)—renamed to `state`
45
+ 5. Mermaid import race condition—caching promise instead of module
46
+
47
+ ## What We Tried
48
+
49
+ Had the work implemented successfully. Subagent spawned mid-session executed `git reset --hard HEAD` as part of cleanup routine, destroying all 26 modified files in one command. Recovered by re-implementing changes from notes and code review feedback.
50
+
51
+ ## Root Cause Analysis
52
+
53
+ The incident happened because there was no git safety protocol when spawning subagents. The subagent was clearing uncommitted work thinking it was cleaning up, not realizing the changes hadn't been committed yet. No blocking commit before delegation meant work was vulnerable.
54
+
55
+ The optimization work itself: good decisions across the board. Shallow equality catches the most common re-render patterns. Lazy loading only targets heavy libraries (mermaid is 2.4MB—absolutely needs deferral). Pagination capping is necessary given unbounded message streams. Code splitting is strategic: vendor chunks reduce main bundle pressure.
56
+
57
+ ## Lessons Learned
58
+
59
+ 1. **Always commit before spawning subagents.** Even exploratory work needs a safety commit. Do not delegate with uncommitted changes in working tree.
60
+ 2. **Shallow equality is the most impactful optimization.** It catches destructured store selectors without requiring component restructuring. Worth doing broadly.
61
+ 3. **Lazy load by file size threshold.** Mermaid (2.4MB), markdown renderer (175KB), and xterm (344KB) were obvious candidates. Sub-100KB libraries don't justify the async boundary cost.
62
+ 4. **Pagination is non-negotiable for unbounded collections.** Streaming chat creates thousands of messages. Without cap, DOM pressure degrades linearly.
63
+ 5. **Code review catches subtle bugs.** The Rules of Hooks violation and race condition were hard to spot in original implementation but obvious in review.
64
+
65
+ ## Next Steps
66
+
67
+ 1. Commit optimizations with `git commit -m "perf: frontend memory optimization — shallow equality, lazy loading, pagination"`
68
+ 2. Test pagination UX on low-end devices to verify load-more doesn't feel janky
69
+ 3. Monitor bundle size in CI to catch regression
70
+ 4. Document code splitting strategy in `docs/performance.md`
71
+
72
+ **Commits:** 26 files modified, 178 insertions, 88 deletions
73
+ **Bundle impact:** Initial +0% (495KB), lazy vendor chunks defer 3.5MB to first use
@@ -6,9 +6,19 @@ All notable changes to PPM are documented here. Format follows [Keep a Changelog
6
6
 
7
7
  ---
8
8
 
9
- ## [Unreleased] — Git-Graph Stash Management, Rebase, Conflict Resolution + Worktree CRUD
9
+ ## [Unreleased] — Frontend Memory Optimization + Git-Graph Stash Management, Rebase, Conflict Resolution + Worktree CRUD
10
10
 
11
11
  ### Added
12
+ - **Frontend Memory & Performance Optimizations** — Reduce re-renders, lazy-load heavy components, code splitting
13
+ - **useShallow pattern:** All destructured Zustand store calls now use `useShallow` (36 usage sites) to prevent re-renders on object mutations
14
+ - **React.memo wrapping:** 10 heavy components memoized (CodeEditor, MessageBubble, ProjectBar, ProjectAvatar, TerminalTab, PanelLayout, Sidebar, StatusBar, StatusBarEntry, TabBar, TreeNode)
15
+ - **Lazy loading:** MarkdownRenderer lazy-loaded from 3 sites, CodeMirror on-demand in postgres-viewer
16
+ - **Dynamic import:** Mermaid loaded on-demand in markdown-code-block (only when needed)
17
+ - **Code splitting:** 5 vendor chunks in vite.config.ts (monaco, mermaid, xterm, markdown, ui libraries) for better caching
18
+ - **Chat pagination:** Message history limited to 50 per page with load-more button
19
+ - **Message cap:** Team activity capped at 500 messages to prevent unbounded growth
20
+
21
+ ### Added (Git-Graph)
12
22
  - **Git Stash Management** — Toolbar popover for interactive stash operations
13
23
  - List all stashes with index, hash (abbreviated), and message
14
24
  - Apply/Pop/Drop actions per stash via context menu or action buttons
@@ -1897,6 +1897,42 @@ Would require:
1897
1897
 
1898
1898
  ---
1899
1899
 
1900
+ ## Frontend Performance Optimization (v0.9.86+)
1901
+
1902
+ ### Memory & Re-Render Reduction
1903
+
1904
+ **1. useShallow Pattern (Zustand)**
1905
+ - All destructured store selectors wrapped in `useShallow()` (36 sites)
1906
+ - Prevents unnecessary re-renders when object properties mutate
1907
+ - Example: `const { messages, addMessage } = chatStore(useShallow(...))`
1908
+
1909
+ **2. Component Memoization (React.memo)**
1910
+ - 10 heavy components wrapped (CodeEditor, MessageBubble, ProjectBar, ProjectAvatar, TerminalTab, PanelLayout, Sidebar, StatusBar, StatusBarEntry, TabBar, TreeNode)
1911
+ - Memoization skips re-renders if props unchanged
1912
+ - Paired with `useCallback` to maintain stable references
1913
+
1914
+ **3. Lazy Loading**
1915
+ - MarkdownRenderer lazy-loaded from 3 sites (reduces initial bundle)
1916
+ - CodeMirror on-demand in postgres-viewer
1917
+ - Mermaid diagram support loaded dynamically only when diagram syntax detected
1918
+
1919
+ **4. Code Splitting (vite.config.ts)**
1920
+ - 5 vendor chunks: `vendor-monaco`, `vendor-mermaid`, `vendor-xterm`, `vendor-markdown`, `vendor-ui`
1921
+ - Heavy libraries (>500KB) in separate chunks for better browser caching
1922
+ - Each chunk independently cacheable and updated
1923
+
1924
+ **5. Chat Pagination & Message Caps**
1925
+ - Chat history loads 50 messages per page with load-more button (prevents DOM bloat)
1926
+ - Team activity capped at 500 messages (prevents unbounded growth)
1927
+
1928
+ ### Benefits
1929
+ - Faster page load (lazy chunks load on-demand)
1930
+ - Reduced re-render cycles (useShallow + memo)
1931
+ - Lower memory footprint (capped message buffers)
1932
+ - Better caching (vendor chunk stability across versions)
1933
+
1934
+ ---
1935
+
1900
1936
  ## Error Handling Strategy
1901
1937
 
1902
1938
  | Layer | Error Type | Handling |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hienlh/ppm",
3
- "version": "0.9.93",
3
+ "version": "0.9.94",
4
4
  "description": "Personal Project Manager — mobile-first web IDE with AI assistance",
5
5
  "author": "hienlh",
6
6
  "license": "MIT",
@@ -1,10 +1,12 @@
1
- import { useEffect, useRef, useState, useMemo, useCallback } from "react";
1
+ import { useEffect, useRef, useState, useMemo, useCallback, memo, lazy, Suspense } from "react";
2
2
  import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
3
3
  import { getAuthToken } from "@/lib/api-client";
4
4
  import type { ChatMessage, ChatEvent } from "../../../types/chat";
5
5
  import type { SessionPhase } from "../../../types/api";
6
6
  import { ToolCard } from "./tool-cards";
7
- import { MarkdownRenderer } from "@/components/shared/markdown-renderer";
7
+ const MarkdownRenderer = lazy(() =>
8
+ import("@/components/shared/markdown-renderer").then((m) => ({ default: m.MarkdownRenderer }))
9
+ );
8
10
  import { cn, basename } from "@/lib/utils";
9
11
 
10
12
  import {
@@ -67,6 +69,34 @@ export function MessageList({
67
69
  }: MessageListProps) {
68
70
  // Scroll handled by StickToBottom wrapper — no manual scroll logic needed
69
71
 
72
+ const PAGE_SIZE = 50;
73
+ const [visibleCount, setVisibleCount] = useState(PAGE_SIZE);
74
+
75
+ // Reset visible count when conversation identity changes (not on every streaming tick)
76
+ const conversationId = messages[0]?.id;
77
+ useEffect(() => { setVisibleCount(PAGE_SIZE); }, [conversationId]);
78
+
79
+ const filtered = useMemo(() => messages.filter((msg) => {
80
+ const hasContent = msg.content && msg.content.trim().length > 0;
81
+ const hasEvents = msg.events && msg.events.length > 0;
82
+ // User bubbles only render text — hide SDK tool-result user messages
83
+ // that have no text content (their events are merged into assistant)
84
+ if (msg.role === "user") return hasContent;
85
+ return hasContent || hasEvents;
86
+ }), [messages]);
87
+
88
+ const displayed = useMemo(() => {
89
+ const start = Math.max(0, filtered.length - visibleCount);
90
+ return filtered.slice(start);
91
+ }, [filtered, visibleCount]);
92
+
93
+ const hasMore = visibleCount < filtered.length;
94
+
95
+ // Stable fork handler — avoids new closure per message (preserves MessageBubble memo)
96
+ const handleFork = useCallback((msgContent: string, msgId: string | undefined) => {
97
+ onFork?.(msgContent, msgId);
98
+ }, [onFork]);
99
+
70
100
  if (messagesLoading) {
71
101
  return (
72
102
  <div className="flex flex-col items-center justify-center h-full gap-3 text-text-secondary">
@@ -85,32 +115,30 @@ export function MessageList({
85
115
  );
86
116
  }
87
117
 
88
- const filtered = useMemo(() => messages.filter((msg) => {
89
- const hasContent = msg.content && msg.content.trim().length > 0;
90
- const hasEvents = msg.events && msg.events.length > 0;
91
- // User bubbles only render text — hide SDK tool-result user messages
92
- // that have no text content (their events are merged into assistant)
93
- if (msg.role === "user") return hasContent;
94
- return hasContent || hasEvents;
95
- }), [messages]);
96
-
97
118
  return (
98
119
  <div className="relative flex-1 overflow-hidden flex flex-col min-h-0">
99
120
  <StickToBottom className="flex-1 overflow-y-auto overflow-x-hidden" resize="smooth" initial="instant">
100
121
  <StickToBottom.Content className="p-4 space-y-4">
101
- {filtered.map((msg, idx) => (
122
+ {hasMore && (
123
+ <button onClick={() => setVisibleCount((c) => c + PAGE_SIZE)}
124
+ className="w-full py-2 text-xs text-text-secondary hover:text-text-primary bg-surface-elevated/50 hover:bg-surface-elevated rounded-md border border-border/50 transition-colors">
125
+ Load {Math.min(PAGE_SIZE, filtered.length - visibleCount)} more messages...
126
+ </button>
127
+ )}
128
+ {displayed.map((msg, idx) => {
129
+ const globalIdx = filtered.length - displayed.length + idx;
130
+ const prevMsg = globalIdx > 0 ? filtered[globalIdx - 1] : undefined;
131
+ return (
102
132
  <MessageBubble
103
133
  key={msg.id}
104
134
  message={msg}
105
135
  isStreaming={isStreaming && msg.id.startsWith("streaming-")}
106
136
  projectName={projectName}
107
- onFork={msg.role === "user" && onFork ? () => {
108
- // Pass the SDK UUID of the previous assistant message for fork (JSONL-level message ID)
109
- const prevMsg = idx > 0 ? filtered[idx - 1] : undefined;
110
- onFork(msg.content, prevMsg?.sdkUuid ?? prevMsg?.id);
111
- } : undefined}
137
+ onFork={msg.role === "user" && onFork ? handleFork : undefined}
138
+ prevMsgId={prevMsg?.sdkUuid ?? prevMsg?.id}
112
139
  />
113
- ))}
140
+ );
141
+ })}
114
142
 
115
143
  {pendingApproval && (
116
144
  pendingApproval.tool === "AskUserQuestion"
@@ -142,10 +170,15 @@ function ScrollToBottomButton() {
142
170
  );
143
171
  }
144
172
 
145
- function MessageBubble({ message, isStreaming, projectName, onFork }: { message: ChatMessage; isStreaming: boolean; projectName?: string; onFork?: () => void }) {
173
+ const MessageBubble = memo(function MessageBubble({ message, isStreaming, projectName, onFork, prevMsgId }: {
174
+ message: ChatMessage; isStreaming: boolean; projectName?: string;
175
+ onFork?: (content: string, messageId: string | undefined) => void;
176
+ prevMsgId?: string
177
+ }) {
146
178
  if (message.role === "user") {
179
+ const handleFork = onFork ? () => onFork(message.content, prevMsgId) : undefined;
147
180
  return (
148
- <UserBubble content={message.content} projectName={projectName} onFork={onFork} />
181
+ <UserBubble content={message.content} projectName={projectName} onFork={handleFork} />
149
182
  );
150
183
  }
151
184
 
@@ -175,7 +208,7 @@ function MessageBubble({ message, isStreaming, projectName, onFork }: { message:
175
208
  )}
176
209
  </div>
177
210
  );
178
- }
211
+ });
179
212
 
180
213
  /** Image extensions that can be previewed inline */
181
214
  const IMAGE_EXTS = new Set([".png", ".jpg", ".jpeg", ".gif", ".webp"]);
@@ -843,7 +876,11 @@ function stripTeammateMessages(text: string): string {
843
876
  function MarkdownContent({ content, projectName, isStreaming }: { content: string; projectName?: string; isStreaming?: boolean }) {
844
877
  const cleaned = stripTeammateMessages(content);
845
878
  if (!cleaned) return null;
846
- return <MarkdownRenderer content={cleaned} projectName={projectName} codeActions isStreaming={isStreaming} />;
879
+ return (
880
+ <Suspense fallback={<div className="animate-pulse h-4 bg-muted rounded" />}>
881
+ <MarkdownRenderer content={cleaned} projectName={projectName} codeActions isStreaming={isStreaming} />
882
+ </Suspense>
883
+ );
847
884
  }
848
885
 
849
886
  /* ToolCard, ToolSummary, ToolDetails extracted to ./tool-cards.tsx */
@@ -2,8 +2,10 @@
2
2
  * Tool card components for chat message rendering.
3
3
  * Handles summary + details for all SDK tool types.
4
4
  */
5
- import { useState, useMemo } from "react";
6
- import { MarkdownRenderer } from "@/components/shared/markdown-renderer";
5
+ import { useState, useMemo, lazy, Suspense } from "react";
6
+ const MarkdownRenderer = lazy(() =>
7
+ import("@/components/shared/markdown-renderer").then((m) => ({ default: m.MarkdownRenderer }))
8
+ );
7
9
  import {
8
10
  ChevronDown,
9
11
  ChevronRight,
@@ -20,6 +22,7 @@ import {
20
22
  Columns2,
21
23
  } from "lucide-react";
22
24
  import type { ChatEvent } from "../../../types/chat";
25
+ import { useShallow } from "zustand/react/shallow";
23
26
  import { useTabStore } from "@/stores/tab-store";
24
27
  import { basename } from "@/lib/utils";
25
28
 
@@ -159,7 +162,7 @@ function ToolDetails({
159
162
  projectName?: string;
160
163
  }) {
161
164
  const s = (v: unknown) => String(v ?? "");
162
- const { openTab } = useTabStore();
165
+ const { openTab } = useTabStore(useShallow((state) => ({ openTab: state.openTab })));
163
166
 
164
167
  /** Open a file in a new editor tab */
165
168
  const openFile = (filePath: string) => {
@@ -451,7 +454,11 @@ function SubagentChildren({ events, projectName }: { events: ChatEvent[]; projec
451
454
 
452
455
  /** Inline markdown renderer for tool details (prompt, result) */
453
456
  function MiniMarkdown({ content, maxHeight = "max-h-48" }: { content: string; maxHeight?: string }) {
454
- return <MarkdownRenderer content={content} className={`text-text-secondary overflow-auto ${maxHeight}`} />;
457
+ return (
458
+ <Suspense fallback={<div className="animate-pulse h-4 bg-muted rounded" />}>
459
+ <MarkdownRenderer content={content} className={`text-text-secondary overflow-auto ${maxHeight}`} />
460
+ </Suspense>
461
+ );
455
462
  }
456
463
 
457
464
 
@@ -1,5 +1,6 @@
1
1
  import { useState, useCallback, useMemo, useRef, memo, useEffect } from "react";
2
2
  import { Loader2, ChevronLeft, ChevronRight, ChevronUp, ChevronDown, Trash2, Plus, Search, X, Eye, Filter, Pin, PinOff, Columns3 } from "lucide-react";
3
+ import { useShallow } from "zustand/react/shallow";
3
4
  import { useTabStore } from "@/stores/tab-store";
4
5
  import type { DbColumnInfo } from "./use-database";
5
6
  import { ExportButton } from "./export-button";
@@ -42,7 +43,7 @@ export function DataGrid({
42
43
  const [insertValues, setInsertValues] = useState<Record<string, string>>({});
43
44
  const [insertError, setInsertError] = useState<string | null>(null);
44
45
  const [confirmBulkDelete, setConfirmBulkDelete] = useState(false);
45
- const { openTab } = useTabStore();
46
+ const { openTab } = useTabStore(useShallow((s) => ({ openTab: s.openTab })));
46
47
  const openCellViewer = useCallback((cell: { col: string; value: string }) => {
47
48
  openTab({
48
49
  type: "editor",