@hienlh/ppm 0.18.9 → 0.18.11

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 (596) hide show
  1. package/.spike/cdp-client.ts +116 -0
  2. package/.spike/cleanup-ppm-workspace.mjs +44 -0
  3. package/.spike/group-protected-probe.ts +21 -0
  4. package/.spike/pip-eval.ts +17 -0
  5. package/.spike/pip-page-helpers.js +119 -0
  6. package/.spike/pip-results-editor-glue.json +105 -0
  7. package/.spike/pip-results-editor.json +158 -0
  8. package/.spike/pip-results-input-isolation.json +28 -0
  9. package/.spike/pip-results-key-probe.json +58 -0
  10. package/.spike/pip-results-listener-survival.json +98 -0
  11. package/.spike/pip-results-listeners.json +36 -0
  12. package/.spike/pip-results-pagehide.json +92 -0
  13. package/.spike/pip-results-resize.json +104 -0
  14. package/.spike/pip-results-terminal-debug.json +99 -0
  15. package/.spike/pip-results-terminal-v2.json +191 -0
  16. package/.spike/pip-results-terminal.json +220 -0
  17. package/.spike/pip-results-xterm-trace.json +73 -0
  18. package/.spike/pip-spike-common.ts +83 -0
  19. package/.spike/pip-spike-editor-glue.ts +82 -0
  20. package/.spike/pip-spike-editor.ts +129 -0
  21. package/.spike/pip-spike-input-isolation.ts +38 -0
  22. package/.spike/pip-spike-key-probe.ts +34 -0
  23. package/.spike/pip-spike-listener-survival.ts +54 -0
  24. package/.spike/pip-spike-listeners.ts +19 -0
  25. package/.spike/pip-spike-pagehide.ts +59 -0
  26. package/.spike/pip-spike-resize.ts +42 -0
  27. package/.spike/pip-spike-setup.ts +63 -0
  28. package/.spike/pip-spike-terminal-debug.ts +81 -0
  29. package/.spike/pip-spike-terminal-v2.ts +139 -0
  30. package/.spike/pip-spike-terminal.ts +169 -0
  31. package/.spike/pip-spike-xterm-trace.ts +36 -0
  32. package/.spike/probe-drag-memo-error.mjs +67 -0
  33. package/.spike/read-dev-db.ts +15 -0
  34. package/.spike/scratch-editor-target.ts +7 -0
  35. package/.spike/smoke-unified-chrome-pip.mjs +353 -0
  36. package/.spike/tunnel.err +63 -42
  37. package/.spike/vite-restart.err +663 -0
  38. package/.spike/w.err +569 -94
  39. package/CHANGELOG.md +25 -0
  40. package/assets/skills/ppm/SKILL.md +1 -1
  41. package/assets/skills/ppm/references/http-api.md +1 -1
  42. package/dist/web/assets/adaptive-context-menu-DGCKKeTy.js +1 -0
  43. package/dist/web/assets/ai-resource-editor-BOHHTTe7.js +1 -0
  44. package/dist/web/assets/ai-settings-section-BigC_nZR.js +1 -0
  45. package/dist/web/assets/{angular-html-Ctz0zN01.js → angular-html-BYPaydvh.js} +1 -1
  46. package/dist/web/assets/{angular-ts-QXHpgB1C.js → angular-ts-Bb4AMKlk.js} +1 -1
  47. package/dist/web/assets/api-client-DTZEgjBQ.js +1 -0
  48. package/dist/web/assets/{api-settings-kQHV6pYZ.js → api-settings-Bd3M4YpI.js} +1 -1
  49. package/dist/web/assets/api-settings-kd1j4iR8.js +1 -0
  50. package/dist/web/assets/{api-themes-CrK0tkW_.js → api-themes-Cpx419SL.js} +1 -1
  51. package/dist/web/assets/{apl-b9KVk-O4.js → apl-DB2VjFCS.js} +1 -1
  52. package/dist/web/assets/architecture-PBZL5I3N-8dCMT9r7.js +1 -0
  53. package/dist/web/assets/{astro-BlFEeGYr.js → astro-BeoJdVpk.js} +1 -1
  54. package/dist/web/assets/{audio-preview-CZLmhf__.js → audio-preview-BgFuTn-0.js} +1 -1
  55. package/dist/web/assets/{blade-CGkj2MUI.js → blade-Cf2q1sPf.js} +1 -1
  56. package/dist/web/assets/{bsl-CVsbb6ap.js → bsl-W1kOaNpu.js} +1 -1
  57. package/dist/web/assets/{button-BA_fJOtX.js → button-5zTrkEoN.js} +1 -1
  58. package/dist/web/assets/{cairo-BbIHl2iE.js → cairo-BoAZFcje.js} +1 -1
  59. package/dist/web/assets/chat-tab-E3bf_V8X.js +15 -0
  60. package/dist/web/assets/chevron-down-Dp0rpHb8.js +1 -0
  61. package/dist/web/assets/clipboard-7fw0Shic.js +1 -0
  62. package/dist/web/assets/{cobol-BWSGjmWb.js → cobol-B0Nm7RXj.js} +1 -1
  63. package/dist/web/assets/code-editor-BUlM2gXk.js +10 -0
  64. package/dist/web/assets/{coffee-CWGNOxds.js → coffee-BakMn3on.js} +1 -1
  65. package/dist/web/assets/{conflict-editor-CdH_h0vD.js → conflict-editor-BUs5k9q_.js} +4 -4
  66. package/dist/web/assets/context-menu-Bq92km4s.js +1 -0
  67. package/dist/web/assets/{cpp-DHGV24J2.js → cpp-BRAJQHK2.js} +1 -1
  68. package/dist/web/assets/{crystal-Vlwf2UMP.js → crystal-kqqO3zkK.js} +1 -1
  69. package/dist/web/assets/{csv-preview-CY_454vd.js → csv-preview-ByoDgLAZ.js} +1 -1
  70. package/dist/web/assets/{data-grid-overlay-editor-D9il5DZS.js → data-grid-overlay-editor-Brkf9z41.js} +1 -1
  71. package/dist/web/assets/database-viewer-Btc3h8Yu.js +1 -0
  72. package/dist/web/assets/dialog-DwtGlowE.js +1 -0
  73. package/dist/web/assets/diff-viewer-CZaDnR4w.js +4 -0
  74. package/dist/web/assets/docx-preview-DAD_vg59.js +12 -0
  75. package/dist/web/assets/{edge-Bhqk64zS.js → edge-Cq1ZKZuE.js} +1 -1
  76. package/dist/web/assets/{edit-diff-preview-gq6ALf2B.js → edit-diff-preview-B_VE7ppQ.js} +1 -1
  77. package/dist/web/assets/{elixir-D466WCFm.js → elixir-BM_JoJRk.js} +1 -1
  78. package/dist/web/assets/{elm-Bfs2qNPR.js → elm-Dw7oO08Y.js} +1 -1
  79. package/dist/web/assets/{erb-WI7jUaB6.js → erb-CfKOGAfk.js} +1 -1
  80. package/dist/web/assets/{erlang-BCBe8nCc.js → erlang-DlDmGVeE.js} +1 -1
  81. package/dist/web/assets/{esm-CZs9WVDO.js → esm-DGAP0GFh.js} +1 -1
  82. package/dist/web/assets/explorer-body-9aBmyVpZ.js +2 -0
  83. package/dist/web/assets/explorer-window-content-DRtqoSPA.js +1 -0
  84. package/dist/web/assets/extension-store-Cpfh2vMm.js +1 -0
  85. package/dist/web/assets/{extension-webview-CuOQrHIS.js → extension-webview-B2OnjuCu.js} +2 -2
  86. package/dist/web/assets/external-link-B8BynhvY.js +1 -0
  87. package/dist/web/assets/file-store-aOERIEo-.js +1 -0
  88. package/dist/web/assets/folder-symlink-B8XrtvQK.js +1 -0
  89. package/dist/web/assets/format-bytes-WVcFxJuT.js +1 -0
  90. package/dist/web/assets/{fortran-fixed-form-CXLIv8Ox.js → fortran-fixed-form-XnAhQWxS.js} +1 -1
  91. package/dist/web/assets/{fsharp-CZsYmiw9.js → fsharp-CdNvy0PG.js} +1 -1
  92. package/dist/web/assets/{gdresource-BGRvoWfC.js → gdresource-DxuG5i8i.js} +1 -1
  93. package/dist/web/assets/{git-commit-DH6mY3Km.js → git-commit-3lMonsur.js} +1 -1
  94. package/dist/web/assets/git-log-panel-Co0PBPHC.js +1 -0
  95. package/dist/web/assets/{git-rebase-C5Gql6Ma.js → git-rebase-N8Z9kIMf.js} +1 -1
  96. package/dist/web/assets/gitGraph-HDMCJU4V-2e_8dfbs.js +1 -0
  97. package/dist/web/assets/{glide-data-grid-Cehfgrzc.js → glide-data-grid-C4_zIneN.js} +8 -8
  98. package/dist/web/assets/{glimmer-js-1-6JMJlj.js → glimmer-js-Dp9OXiib.js} +1 -1
  99. package/dist/web/assets/{glimmer-ts-C_Njj0nK.js → glimmer-ts-CDVKBcDj.js} +1 -1
  100. package/dist/web/assets/{glsl-DbkxE1TN.js → glsl-DPJxI4us.js} +1 -1
  101. package/dist/web/assets/{graphql-BK75fiXl.js → graphql-BrFhaQQl.js} +1 -1
  102. package/dist/web/assets/group-chat-tab-rOd8J8GC.js +1 -0
  103. package/dist/web/assets/{hack-DG151vit.js → hack-BzXbDMlA.js} +1 -1
  104. package/dist/web/assets/{haml-DKeIPQWM.js → haml-CPpkX0s5.js} +1 -1
  105. package/dist/web/assets/{handlebars-398uCI13.js → handlebars-BqrApy52.js} +1 -1
  106. package/dist/web/assets/{html-NLWdst5q.js → html-DmjUl-OA.js} +1 -1
  107. package/dist/web/assets/{html-derivative-CJf_Le2z.js → html-derivative-Cnd6XFR5.js} +1 -1
  108. package/dist/web/assets/{http-G1K2FozA.js → http-DMLWYun2.js} +1 -1
  109. package/dist/web/assets/{hurl-WDC6sSBz.js → hurl-CKkCVxbj.js} +1 -1
  110. package/dist/web/assets/{hxml-Dc3jBXuf.js → hxml-CAx19TtQ.js} +1 -1
  111. package/dist/web/assets/image-preview-DHUoylQ1.js +1 -0
  112. package/dist/web/assets/index-BImUaigO.js +52 -0
  113. package/dist/web/assets/index-CSfi6DSU.css +2 -0
  114. package/dist/web/assets/info-3K5VOQVL-BZi1Gha1.js +1 -0
  115. package/dist/web/assets/input-DcEk7mA3.js +1 -0
  116. package/dist/web/assets/{jinja-CWsQj2y0.js → jinja-BmB9YN9l.js} +1 -1
  117. package/dist/web/assets/{jison-CciJg2NW.js → jison-BaVsKKpw.js} +1 -1
  118. package/dist/web/assets/{julia-IOQNJAB7.js → julia-BgVBLveX.js} +1 -1
  119. package/dist/web/assets/{just-BGdHchwS.js → just-C2ENIbl9.js} +1 -1
  120. package/dist/web/assets/keybindings-store-BGIyqXHI.js +1 -0
  121. package/dist/web/assets/keybindings-store-CzV2sPls.js +1 -0
  122. package/dist/web/assets/{latex-D6TOl-L2.js → latex-BiDIWxKL.js} +1 -1
  123. package/dist/web/assets/{liquid-CVSvu_Wn.js → liquid-NxpapreE.js} +1 -1
  124. package/dist/web/assets/loader-circle-gSPVxfvH.js +1 -0
  125. package/dist/web/assets/lock-Did-87UV.js +1 -0
  126. package/dist/web/assets/{lua-CcKiU6HW.js → lua-DVepQvwH.js} +1 -1
  127. package/dist/web/assets/markdown-renderer-CcbR33VG.js +1 -0
  128. package/dist/web/assets/{markdown-renderer-pVzBdkpl.js → markdown-renderer-DlcDhgrF.js} +3 -3
  129. package/dist/web/assets/{marko--UOicMGo.js → marko-CJ2606Xl.js} +1 -1
  130. package/dist/web/assets/{mdc-D690aU2C.js → mdc-D2bjqclE.js} +1 -1
  131. package/dist/web/assets/{media-url-CylYQld1.js → media-url-H17XJF-I.js} +1 -1
  132. package/dist/web/assets/mobile-bottom-sheet-g-r2hCFa.js +1 -0
  133. package/dist/web/assets/mobile-explorer-sheet-SIwQXlwA.js +1 -0
  134. package/dist/web/assets/monitor-Dv8vn_6O.js +1 -0
  135. package/dist/web/assets/{nextflow-DR8PhpF8.js → nextflow-BtJwUbfF.js} +1 -1
  136. package/dist/web/assets/{nginx-Qb9pUk1g.js → nginx-j4gyIhHE.js} +1 -1
  137. package/dist/web/assets/{nim-DLa8R3NS.js → nim-ClH_gjP5.js} +1 -1
  138. package/dist/web/assets/notification-store-DV5QtUTg.js +1 -0
  139. package/dist/web/assets/{number-overlay-editor-D9SkFg1Y.js → number-overlay-editor-C1EqJnWq.js} +1 -1
  140. package/dist/web/assets/overview-panel-vTeeJYVZ.js +1 -0
  141. package/dist/web/assets/packet-RMMSAZCW-o5htEcin.js +1 -0
  142. package/dist/web/assets/panel-store-BSnkjgHt.js +1 -0
  143. package/dist/web/assets/pdf-preview-CqFGRY0Z.js +1 -0
  144. package/dist/web/assets/pencil-BlIIg6RH.js +1 -0
  145. package/dist/web/assets/{perl-AMNGIneF.js → perl-C9H7X7CT.js} +1 -1
  146. package/dist/web/assets/{php-CDsOOxXn.js → php-Bx2U-cQ_.js} +1 -1
  147. package/dist/web/assets/pie-UPGHQEXC-BXAIFSAD.js +1 -0
  148. package/dist/web/assets/play-4MZi1Axp.js +1 -0
  149. package/dist/web/assets/portal-container-context-Cm5HipFr.js +1 -0
  150. package/dist/web/assets/{postgres-viewer-CR6VpTrX.js → postgres-viewer-DHwcQucn.js} +3 -3
  151. package/dist/web/assets/process-table-BGRJNlpn.js +1 -0
  152. package/dist/web/assets/{project-store-B8BkHXy5.js → project-store--nvQiy75.js} +1 -1
  153. package/dist/web/assets/{pug-C2kdkhQ6.js → pug-Cw74sKsE.js} +1 -1
  154. package/dist/web/assets/{qml-DRDkUp75.js → qml-BXD94Eqz.js} +1 -1
  155. package/dist/web/assets/radar-KQ55EAFF-Dbt2Aebl.js +1 -0
  156. package/dist/web/assets/{razor-wY_6wUeC.js → razor-DZ0-Ge0S.js} +1 -1
  157. package/dist/web/assets/refresh-cw-B06yaBrW.js +1 -0
  158. package/dist/web/assets/resolve-theme-CRt_7wpd.js +1 -0
  159. package/dist/web/assets/rotate-ccw-COXu_9oj.js +1 -0
  160. package/dist/web/assets/{rst-BRpofyNs.js → rst-Dtdh0QUj.js} +1 -1
  161. package/dist/web/assets/{ruby-ieATBhWm.js → ruby-BC51BW2Q.js} +1 -1
  162. package/dist/web/assets/{run-in-terminal-CyS5Itj-.js → run-in-terminal-Sd3KyjN4.js} +1 -1
  163. package/dist/web/assets/{sas-CoOOEbOO.js → sas-BN8i4qOU.js} +1 -1
  164. package/dist/web/assets/scroll-area-0gljZ6lF.js +1 -0
  165. package/dist/web/assets/{scss-_nWV0bsb.js → scss-H-QtA7Rz.js} +1 -1
  166. package/dist/web/assets/send-BlyNJAzy.js +1 -0
  167. package/dist/web/assets/{send-to-chat-BcO39BDR.js → send-to-chat-u6m8l-rH.js} +1 -1
  168. package/dist/web/assets/{settings-store-C20lqh0G.js → settings-store-CHY6Kzin.js} +2 -2
  169. package/dist/web/assets/settings-tab-Dic6TKkU.js +1 -0
  170. package/dist/web/assets/settings-tab-IY6K1MiZ.js +3 -0
  171. package/dist/web/assets/{shaderlab-XkqfIfO8.js → shaderlab-CalNsj_x.js} +1 -1
  172. package/dist/web/assets/shallow-DYo6goJ4.js +1 -0
  173. package/dist/web/assets/{shellsession-BKBIqTlM.js → shellsession-BFrcoR8x.js} +1 -1
  174. package/dist/web/assets/{soy-oVDnPzzh.js → soy-CNbku7vP.js} +1 -1
  175. package/dist/web/assets/{sparql-N2VclEsF.js → sparql-BN_orcVp.js} +1 -1
  176. package/dist/web/assets/{sql-query-editor-BPYsJDsn.js → sql-query-editor-C6pC3I8l.js} +1 -1
  177. package/dist/web/assets/sqlite-viewer-DTr5PzUb.js +1 -0
  178. package/dist/web/assets/{stata-BuuClVDg.js → stata-C6a5Ijzq.js} +1 -1
  179. package/dist/web/assets/{surrealql-DE5_63MS.js → surrealql-ClplI_HY.js} +1 -1
  180. package/dist/web/assets/{svelte--YWjlOrK.js → svelte-DNm-UNXB.js} +1 -1
  181. package/dist/web/assets/switch-DKvKya9y.js +1 -0
  182. package/dist/web/assets/system-monitor-body-iwGiCns9.js +2 -0
  183. package/dist/web/assets/system-monitor-tab-DdXQhEel.js +1 -0
  184. package/dist/web/assets/system-monitor-window-content-BUqRsPK2.js +1 -0
  185. package/dist/web/assets/tab-host-window-content-BnqGPu8a.js +1 -0
  186. package/dist/web/assets/{tab-store-DAsvpx4t.js → tab-store-CJuJRapl.js} +1 -1
  187. package/dist/web/assets/team-member-sheet-LkAFSCvb.js +2 -0
  188. package/dist/web/assets/team-member-window-content-T4Q5JI-G.js +1 -0
  189. package/dist/web/assets/{templ-MlpvJx4a.js → templ-CSdqVobu.js} +1 -1
  190. package/dist/web/assets/terminal-tab-Bb2rZegQ.js +3 -0
  191. package/dist/web/assets/{tex-0loAoXJG.js → tex-DzSJ4kcZ.js} +1 -1
  192. package/dist/web/assets/text-wrap-CBT4Gxtg.js +1 -0
  193. package/dist/web/assets/tool-cards-lhqhrDTT.js +8 -0
  194. package/dist/web/assets/trash-2-Bk4yYlck.js +1 -0
  195. package/dist/web/assets/treemap-KZPCXAKY-DUbnl9dz.js +1 -0
  196. package/dist/web/assets/triangle-alert-B8EYecpe.js +1 -0
  197. package/dist/web/assets/{ts-tags-pt-Ug0hK.js → ts-tags-C28w3bTg.js} +1 -1
  198. package/dist/web/assets/{twig-DOePpLOL.js → twig-i5jY3QXB.js} +1 -1
  199. package/dist/web/assets/upload-BNlX8D07.js +1 -0
  200. package/dist/web/assets/{use-blob-url-BKyVxOZw.js → use-blob-url-Dsq9OjTh.js} +1 -1
  201. package/dist/web/assets/{use-monaco-theme-CGucu3fb.js → use-monaco-theme-BZvlsoBv.js} +1 -1
  202. package/dist/web/assets/use-open-team-member-DkiEjFYE.js +1 -0
  203. package/dist/web/assets/{use-websocket-C-Rxv8JN.js → use-websocket-B8R50f7f.js} +1 -1
  204. package/dist/web/assets/{vendor-mermaid-DrwG0NYt.js → vendor-mermaid-By4AVvJY.js} +1 -1
  205. package/dist/web/assets/video-preview-m0-3p7QZ.js +1 -0
  206. package/dist/web/assets/{vue-D6f-rprj.js → vue-Ceyet_lv.js} +1 -1
  207. package/dist/web/assets/{vue-html-DEYRkS1V.js → vue-html-GuU7g_hg.js} +1 -1
  208. package/dist/web/assets/{vue-vine-CxSG2ojf.js → vue-vine-CxXoaJr2.js} +1 -1
  209. package/dist/web/assets/window-store-CsEW6jiE.js +1 -0
  210. package/dist/web/assets/{xml-DhrxbATj.js → xml-CbqOVhkP.js} +1 -1
  211. package/dist/web/assets/{xsl-CU0ZndUO.js → xsl-KfAmCauy.js} +1 -1
  212. package/dist/web/index.html +56 -27
  213. package/dist/web/sw.js +1 -1
  214. package/docs/code-standards.md +8 -0
  215. package/docs/codebase-summary.md +19 -5
  216. package/docs/design-guidelines.md +13 -2
  217. package/docs/project-roadmap.md +9 -0
  218. package/docs/system-architecture.md +83 -6
  219. package/package.json +1 -1
  220. package/src/web/components/chat/file-picker.tsx +5 -2
  221. package/src/web/components/chat/mode-selector.tsx +5 -2
  222. package/src/web/components/chat/model-selector.tsx +5 -2
  223. package/src/web/components/chat/model-thinking-selector.tsx +5 -2
  224. package/src/web/components/chat/provider-selector.tsx +5 -2
  225. package/src/web/components/chat/slash-command-picker.tsx +5 -2
  226. package/src/web/components/editor/code-editor.tsx +13 -3
  227. package/src/web/components/editor/conflict-editor.tsx +9 -0
  228. package/src/web/components/editor/diff-viewer.tsx +9 -0
  229. package/src/web/components/floating-window/floating-window.tsx +23 -15
  230. package/src/web/components/floating-window/pip/pip-caption-button.tsx +108 -0
  231. package/src/web/components/floating-window/pip/pip-focus-target.ts +58 -0
  232. package/src/web/components/floating-window/pip/pip-geometry.ts +46 -0
  233. package/src/web/components/floating-window/pip/pip-host.ts +156 -0
  234. package/src/web/components/floating-window/pip/pip-key-forward.ts +59 -0
  235. package/src/web/components/floating-window/pip/pip-resize-signal.ts +33 -0
  236. package/src/web/components/floating-window/pip/pip-style-copy.ts +108 -0
  237. package/src/web/components/floating-window/pip/pip-support.ts +33 -0
  238. package/src/web/components/floating-window/tab-host-window-content.tsx +80 -0
  239. package/src/web/components/floating-window/use-window-body-element.ts +67 -0
  240. package/src/web/components/floating-window/use-window-drag.ts +5 -0
  241. package/src/web/components/floating-window/use-window-gesture-context.ts +14 -0
  242. package/src/web/components/floating-window/use-window-resize.ts +5 -0
  243. package/src/web/components/floating-window/window-content-registry.ts +4 -16
  244. package/src/web/components/floating-window/window-layer.tsx +1 -2
  245. package/src/web/components/floating-window/window-persistence.ts +9 -2
  246. package/src/web/components/floating-window/window-pip-placeholder.tsx +31 -0
  247. package/src/web/components/floating-window/window-pip-registry.ts +69 -0
  248. package/src/web/components/floating-window/window-skin-chrome.tsx +21 -0
  249. package/src/web/components/floating-window/window-store-types.ts +8 -2
  250. package/src/web/components/layout/reparenting-tab.tsx +136 -0
  251. package/src/web/components/layout/tab-bar.tsx +2 -0
  252. package/src/web/components/layout/tab-pool-collect.ts +38 -2
  253. package/src/web/components/layout/tab-pool-registry.ts +48 -0
  254. package/src/web/components/layout/tab-pool.tsx +54 -168
  255. package/src/web/components/layout/tab-pop-out-menu-item.tsx +42 -0
  256. package/src/web/components/layout/use-window-panel-reconcile.ts +48 -0
  257. package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +16 -7
  258. package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +14 -8
  259. package/src/web/components/system/build-kill-request.ts +28 -1
  260. package/src/web/components/system/chart-scale.ts +3 -3
  261. package/src/web/components/system/kill-confirm-dialog.tsx +44 -28
  262. package/src/web/components/system/metric-chart-canvas.tsx +1 -2
  263. package/src/web/components/system/process-columns-grid.ts +39 -29
  264. package/src/web/components/system/process-group-row.tsx +31 -8
  265. package/src/web/components/system/process-row.tsx +6 -7
  266. package/src/web/components/system/process-table-model.ts +4 -22
  267. package/src/web/components/system/process-table-toolbar.tsx +12 -43
  268. package/src/web/components/system/process-table.tsx +22 -63
  269. package/src/web/components/system/sortable-header.tsx +33 -8
  270. package/src/web/components/system/system-monitor-body.tsx +1 -1
  271. package/src/web/components/system/use-column-widths.ts +91 -0
  272. package/src/web/components/system/use-process-kill.ts +100 -0
  273. package/src/web/components/ui/context-menu.tsx +10 -3
  274. package/src/web/components/ui/dialog.tsx +10 -1
  275. package/src/web/components/ui/dropdown-menu.tsx +10 -3
  276. package/src/web/components/ui/mobile-bottom-sheet.tsx +7 -4
  277. package/src/web/components/ui/portal-container-context.tsx +40 -0
  278. package/src/web/components/ui/select.tsx +3 -1
  279. package/src/web/components/ui/tooltip.tsx +3 -1
  280. package/src/web/hooks/use-terminal.ts +24 -3
  281. package/src/web/stores/panel-store.ts +72 -7
  282. package/src/web/stores/panel-utils.ts +36 -0
  283. package/src/web/stores/window-panel-actions.ts +211 -0
  284. package/src/web/stores/window-panel-persistence.ts +115 -0
  285. package/src/web/stores/window-panel-reconcile.ts +97 -0
  286. package/src/web/styles/globals.css +5 -3
  287. package/src/web/theme/builtin/aurora.ts +8 -5
  288. package/dist/web/assets/ai-resource-editor-DFCuNe-P.js +0 -1
  289. package/dist/web/assets/api-client-CzABTreM.js +0 -1
  290. package/dist/web/assets/api-settings-nblja3Rk.js +0 -1
  291. package/dist/web/assets/architecture-PBZL5I3N-C7ra20CE.js +0 -1
  292. package/dist/web/assets/chat-tab-flMPLoKb.js +0 -15
  293. package/dist/web/assets/code-editor-C2VQbXzw.js +0 -10
  294. package/dist/web/assets/context-menu-Cq0u3SK9.js +0 -1
  295. package/dist/web/assets/database-viewer-BCZOTrPT.js +0 -1
  296. package/dist/web/assets/diff-viewer-BwfMN-nW.js +0 -4
  297. package/dist/web/assets/docx-preview-D3Sdh_aT.js +0 -12
  298. package/dist/web/assets/explorer-body-BKOMM566.js +0 -2
  299. package/dist/web/assets/explorer-window-content-Bbzijk8t.js +0 -1
  300. package/dist/web/assets/format-bytes-Dk7B73B7.js +0 -1
  301. package/dist/web/assets/git-log-panel-DcV7PhqW.js +0 -1
  302. package/dist/web/assets/gitGraph-HDMCJU4V-D4hYLNJU.js +0 -1
  303. package/dist/web/assets/group-chat-tab-BVPYxjz-.js +0 -1
  304. package/dist/web/assets/image-preview-D-Ok10eZ.js +0 -1
  305. package/dist/web/assets/index-CerSxGdf.js +0 -52
  306. package/dist/web/assets/index-DRA_OpWY.css +0 -2
  307. package/dist/web/assets/info-3K5VOQVL-lRlrTlkb.js +0 -1
  308. package/dist/web/assets/keybindings-store-aIY40k2o.js +0 -1
  309. package/dist/web/assets/markdown-renderer-I89kyQhe.js +0 -1
  310. package/dist/web/assets/mobile-bottom-sheet-BUOJgiaZ.js +0 -1
  311. package/dist/web/assets/mobile-explorer-sheet-5Uj9ZMM9.js +0 -1
  312. package/dist/web/assets/notification-store-DJ-OtAG3.js +0 -1
  313. package/dist/web/assets/overview-panel-Cgi3UPY1.js +0 -1
  314. package/dist/web/assets/packet-RMMSAZCW-BIhk1mvp.js +0 -1
  315. package/dist/web/assets/panel-store-DCD82zc1.js +0 -1
  316. package/dist/web/assets/pdf-preview-Di5xHFyT.js +0 -1
  317. package/dist/web/assets/pie-UPGHQEXC-SLS6i76l.js +0 -1
  318. package/dist/web/assets/process-table-DL6shbzt.js +0 -1
  319. package/dist/web/assets/radar-KQ55EAFF-JJ6O1Er_.js +0 -1
  320. package/dist/web/assets/resolve-theme-IdhSR-gx.js +0 -1
  321. package/dist/web/assets/settings-tab-jbiQwozX.js +0 -1
  322. package/dist/web/assets/sqlite-viewer-Bq9ZJA2y.js +0 -1
  323. package/dist/web/assets/system-monitor-body-CKEYWyN9.js +0 -2
  324. package/dist/web/assets/system-monitor-tab-WjhFFHyM.js +0 -1
  325. package/dist/web/assets/system-monitor-window-content-CMiISeoC.js +0 -1
  326. package/dist/web/assets/team-member-sheet-oY5EkH53.js +0 -2
  327. package/dist/web/assets/team-member-window-content-yCBeI_5X.js +0 -1
  328. package/dist/web/assets/terminal-tab-BxHtMR7h.js +0 -3
  329. package/dist/web/assets/tool-cards-BcGrk1zk.js +0 -8
  330. package/dist/web/assets/treemap-KZPCXAKY-Be3KNmns.js +0 -1
  331. package/dist/web/assets/use-open-team-member-DdrRXj-M.js +0 -1
  332. package/dist/web/assets/video-preview-DYAbbmd0.js +0 -1
  333. package/src/web/components/floating-window/default-window-chrome.tsx +0 -71
  334. package/src/web/components/os-explorer/skins/explorer-skin-chrome.tsx +0 -17
  335. package/src/web/components/system/sparkline-canvas.tsx +0 -63
  336. /package/dist/web/assets/{abap-MRbmsXOs.js → abap-C6nB04tn.js} +0 -0
  337. /package/dist/web/assets/{actionscript-3-BDDHKqa4.js → actionscript-3-Vx4rw2sC.js} +0 -0
  338. /package/dist/web/assets/{ada-DpuV9KwV.js → ada-Ccd8vM20.js} +0 -0
  339. /package/dist/web/assets/{andromeeda-D6qUCKJo.js → andromeeda-DnBX5xMg.js} +0 -0
  340. /package/dist/web/assets/{apache-CX1NU_Tl.js → apache-B-9_sx0g.js} +0 -0
  341. /package/dist/web/assets/{apex-B4-aoQ3K.js → apex-_wPVBYoL.js} +0 -0
  342. /package/dist/web/assets/{api-client-BSB14J4w.js → api-client-aQb5hHE5.js} +0 -0
  343. /package/dist/web/assets/{applescript-Dm9NI6KR.js → applescript-CcvD-qr0.js} +0 -0
  344. /package/dist/web/assets/{apply-theme-BH_d4amk.js → apply-theme-BeLRfsSu.js} +0 -0
  345. /package/dist/web/assets/{ara-BGN8GAYn.js → ara-C40Ztoms.js} +0 -0
  346. /package/dist/web/assets/{asciidoc-s_yVuR3t.js → asciidoc-CybYkJV4.js} +0 -0
  347. /package/dist/web/assets/{asm-BzQdSvTe.js → asm-9eOKwzR0.js} +0 -0
  348. /package/dist/web/assets/{aurora-x-BJTLETG_.js → aurora-x-B5ZH4t7t.js} +0 -0
  349. /package/dist/web/assets/{awk-BQEGWxvX.js → awk-Dqilob-2.js} +0 -0
  350. /package/dist/web/assets/{ayu-dark-CpTORmLc.js → ayu-dark-CPgRhf5r.js} +0 -0
  351. /package/dist/web/assets/{ayu-light-D8PVj-e6.js → ayu-light-Do06Q52f.js} +0 -0
  352. /package/dist/web/assets/{ayu-mirage-DyElQHtm.js → ayu-mirage-_BhxcrxY.js} +0 -0
  353. /package/dist/web/assets/{ballerina-C82NkRtj.js → ballerina-DDwK54QW.js} +0 -0
  354. /package/dist/web/assets/{bat-0X7DtGI-.js → bat-BGyGlzGk.js} +0 -0
  355. /package/dist/web/assets/{beancount-Xgvy6YRl.js → beancount-CRWBdapI.js} +0 -0
  356. /package/dist/web/assets/{berry-CCT2pZcQ.js → berry-DbSFn2gO.js} +0 -0
  357. /package/dist/web/assets/{bibtex-CpXgZ9M_.js → bibtex-QpsV9gm9.js} +0 -0
  358. /package/dist/web/assets/{bicep-fkeSA62l.js → bicep-CxS8CUve.js} +0 -0
  359. /package/dist/web/assets/{bird2-Co7SeAI8.js → bird2-CuZhQzYR.js} +0 -0
  360. /package/dist/web/assets/{bot-CunPuEdW.js → bot-D6sx11y0.js} +0 -0
  361. /package/dist/web/assets/{c-BpmrTvJh.js → c-9bnDqE3g.js} +0 -0
  362. /package/dist/web/assets/{c3-BZ_Z0RrR.js → c3-JMZTrOhA.js} +0 -0
  363. /package/dist/web/assets/{cadence-B0GBC8OF.js → cadence-DsDNayJS.js} +0 -0
  364. /package/dist/web/assets/{can-open-in-ppm-omBzUBBq.js → can-open-in-ppm-BJfpynya.js} +0 -0
  365. /package/dist/web/assets/{catppuccin-frappe-DIt_xR7m.js → catppuccin-frappe-DYIKXXBe.js} +0 -0
  366. /package/dist/web/assets/{catppuccin-latte-Dw40AcqA.js → catppuccin-latte-IYYOnUJ8.js} +0 -0
  367. /package/dist/web/assets/{catppuccin-macchiato-BJ7DfVEF.js → catppuccin-macchiato-BrJ_7mfS.js} +0 -0
  368. /package/dist/web/assets/{catppuccin-mocha-NI2lfXzU.js → catppuccin-mocha-gzSu7v7-.js} +0 -0
  369. /package/dist/web/assets/{chevron-right-BLOtX8Vy.js → chevron-right-De-Wz5Lz.js} +0 -0
  370. /package/dist/web/assets/{circle-hjUGru6R.js → circle-BCy0AUCo.js} +0 -0
  371. /package/dist/web/assets/{clarity-F39L0xMJ.js → clarity-DLzsC0Hb.js} +0 -0
  372. /package/dist/web/assets/{clock-CvhF4DJP.js → clock-C1CQUQPZ.js} +0 -0
  373. /package/dist/web/assets/{clojure-DloMkd8K.js → clojure-DS1BvoTt.js} +0 -0
  374. /package/dist/web/assets/{cmake-CwerJun_.js → cmake-CDvsJwYp.js} +0 -0
  375. /package/dist/web/assets/{code-DDPoqVri.js → code-DJgYrFzm.js} +0 -0
  376. /package/dist/web/assets/{codeowners-CQyVz4Qf.js → codeowners-BjsYiflJ.js} +0 -0
  377. /package/dist/web/assets/{codeql-Bb229zuY.js → codeql-CG-7ARat.js} +0 -0
  378. /package/dist/web/assets/{common-lisp-KMlIwq5I.js → common-lisp-DSO1Lbh9.js} +0 -0
  379. /package/dist/web/assets/{copy-DKn11Pye.js → copy-BewHj66E.js} +0 -0
  380. /package/dist/web/assets/{coq-DHX_3IIo.js → coq-DYdh3oP_.js} +0 -0
  381. /package/dist/web/assets/{csharp-CEmCZv9Q.js → csharp-DxEBz5s0.js} +0 -0
  382. /package/dist/web/assets/{css-CqZqBD05.js → css-dal3Q-8C.js} +0 -0
  383. /package/dist/web/assets/{csv-l1g_V5MV.js → csv-CpqjMGiA.js} +0 -0
  384. /package/dist/web/assets/{csv-parser-CnS0wqr6.js → csv-parser-3umBA4NY.js} +0 -0
  385. /package/dist/web/assets/{cue-BrxRmilg.js → cue-DinoJ_5k.js} +0 -0
  386. /package/dist/web/assets/{cypher-rZkKwqZc.js → cypher-DzxqmkUW.js} +0 -0
  387. /package/dist/web/assets/{d-BALR-QuP.js → d-BxNwJuEC.js} +0 -0
  388. /package/dist/web/assets/{dark-plus-Cj7rVwh-.js → dark-plus-Clmv3V-0.js} +0 -0
  389. /package/dist/web/assets/{dart-CWQcdkXk.js → dart-BWVtXD35.js} +0 -0
  390. /package/dist/web/assets/{data-grid-types-CYvF-xK5.js → data-grid-types-B4n51Fck.js} +0 -0
  391. /package/dist/web/assets/{database-BH0urvbs.js → database-Y3ESDK4-.js} +0 -0
  392. /package/dist/web/assets/{dax-BNHD-QBA.js → dax-DsImuWgJ.js} +0 -0
  393. /package/dist/web/assets/{desktop-CN0IXPrP.js → desktop-B0jJuG2r.js} +0 -0
  394. /package/dist/web/assets/{diff-BtOXapES.js → diff-CWqAAxoE.js} +0 -0
  395. /package/dist/web/assets/{dist-DB1Oaqcc.js → dist-B-EXdSYE.js} +0 -0
  396. /package/dist/web/assets/{dist-Di6G_5jU.js → dist-C0eNNfbt.js} +0 -0
  397. /package/dist/web/assets/{docker-DNsAo_qO.js → docker-CYFCJdIL.js} +0 -0
  398. /package/dist/web/assets/{dotenv-D6Pl6aOU.js → dotenv-CKDUjURg.js} +0 -0
  399. /package/dist/web/assets/{download-zCATy4x4.js → download-DG3_9xXy.js} +0 -0
  400. /package/dist/web/assets/{dracula-C3m-w12D.js → dracula-CSoPspHE.js} +0 -0
  401. /package/dist/web/assets/{dracula-soft-BYqvXm6Z.js → dracula-soft-BuFjw5L1.js} +0 -0
  402. /package/dist/web/assets/{dream-maker-DZAPbPTi.js → dream-maker-B6PdDRZu.js} +0 -0
  403. /package/dist/web/assets/{emacs-lisp-Ao5SW8ay.js → emacs-lisp-CMcXvD0G.js} +0 -0
  404. /package/dist/web/assets/{everforest-dark-uC8QYotQ.js → everforest-dark-cU8-wYzi.js} +0 -0
  405. /package/dist/web/assets/{everforest-light-CTgsVNMr.js → everforest-light-KhorllqK.js} +0 -0
  406. /package/dist/web/assets/{fennel-WuBw6IR1.js → fennel-BCELZK87.js} +0 -0
  407. /package/dist/web/assets/{file-exclamation-point-COAwgNGw.js → file-exclamation-point-CZ4DiR75.js} +0 -0
  408. /package/dist/web/assets/{fish-Byy9dusu.js → fish-BQc0y_jy.js} +0 -0
  409. /package/dist/web/assets/{fluent-CDxb_sgw.js → fluent-BTINnwF6.js} +0 -0
  410. /package/dist/web/assets/{fortran-free-form-DKWSTamT.js → fortran-free-form-BQim9wDz.js} +0 -0
  411. /package/dist/web/assets/{gdscript-prYHMgtD.js → gdscript-03w5u8un.js} +0 -0
  412. /package/dist/web/assets/{gdshader-D-RM-SQk.js → gdshader-Dp2daNzU.js} +0 -0
  413. /package/dist/web/assets/{genie-CowkJhej.js → genie-CoNcjPiD.js} +0 -0
  414. /package/dist/web/assets/{gherkin-CLFpoVYJ.js → gherkin-NrPOSPQL.js} +0 -0
  415. /package/dist/web/assets/{github-dark-default-BK_1Qgb8.js → github-dark-default-DOKw-1cI.js} +0 -0
  416. /package/dist/web/assets/{github-dark-dimmed-Cv9lj6f-.js → github-dark-dimmed-DJRNMkEA.js} +0 -0
  417. /package/dist/web/assets/{github-dark-high-contrast-CW5ZgIDh.js → github-dark-high-contrast-Bj7jJ_Gs.js} +0 -0
  418. /package/dist/web/assets/{github-dark-nW2F-RLd.js → github-dark-z4RE9ZqA.js} +0 -0
  419. /package/dist/web/assets/{github-light-D4PqBHGZ.js → github-light-B4k_yMFu.js} +0 -0
  420. /package/dist/web/assets/{github-light-default-B-cHtJJV.js → github-light-default-BHc-Uzhs.js} +0 -0
  421. /package/dist/web/assets/{github-light-high-contrast-Dt0HJq50.js → github-light-high-contrast-CHisIywL.js} +0 -0
  422. /package/dist/web/assets/{gleam-DwSyXJWP.js → gleam-CfbypZ4q.js} +0 -0
  423. /package/dist/web/assets/{gn-Cv5WEzHc.js → gn-YoAjyhJa.js} +0 -0
  424. /package/dist/web/assets/{gnuplot-D7bRdY_M.js → gnuplot-xVkc1f1c.js} +0 -0
  425. /package/dist/web/assets/{go-DiRVJ22U.js → go-GPCwP3Gu.js} +0 -0
  426. /package/dist/web/assets/{groovy-BHAgLTeO.js → groovy-Crh4Yww9.js} +0 -0
  427. /package/dist/web/assets/{gruvbox-dark-hard-C8QZ3gzY.js → gruvbox-dark-hard-BluUFuDS.js} +0 -0
  428. /package/dist/web/assets/{gruvbox-dark-medium-Dp1eeid2.js → gruvbox-dark-medium-Cxk1FWQp.js} +0 -0
  429. /package/dist/web/assets/{gruvbox-dark-soft-Cwq0dWVj.js → gruvbox-dark-soft-CHXMiFmj.js} +0 -0
  430. /package/dist/web/assets/{gruvbox-light-hard-CLQCq2-w.js → gruvbox-light-hard-B6eKPjAD.js} +0 -0
  431. /package/dist/web/assets/{gruvbox-light-medium-APHsh6Mo.js → gruvbox-light-medium-pDwMm1ut.js} +0 -0
  432. /package/dist/web/assets/{gruvbox-light-soft-CX-fYc64.js → gruvbox-light-soft-Cxu6kHqK.js} +0 -0
  433. /package/dist/web/assets/{haskell-C4Tw-TDa.js → haskell-C6JKHjbK.js} +0 -0
  434. /package/dist/web/assets/{haxe-B7fSduTQ.js → haxe-BuBNdF0a.js} +0 -0
  435. /package/dist/web/assets/{hcl-Bi3ho6yC.js → hcl-DZpDdGj2.js} +0 -0
  436. /package/dist/web/assets/{hjson-BXwae2M1.js → hjson-CBkhYRuo.js} +0 -0
  437. /package/dist/web/assets/{hlsl-CRuQLBSa.js → hlsl-C5nMxnp9.js} +0 -0
  438. /package/dist/web/assets/{horizon-DtdQ6qjv.js → horizon-VAFvA6R6.js} +0 -0
  439. /package/dist/web/assets/{horizon-bright-BXSF_uKq.js → horizon-bright-BC0xn7T3.js} +0 -0
  440. /package/dist/web/assets/{houston-C6ASpUjT.js → houston-BxrUivmz.js} +0 -0
  441. /package/dist/web/assets/{hy-BrWBHtHY.js → hy-DLtOXoaH.js} +0 -0
  442. /package/dist/web/assets/{image-gallery-CGvlv8zR.js → image-gallery-Gnf7TdCR.js} +0 -0
  443. /package/dist/web/assets/{imba-C_JJXckG.js → imba-BdpFXM2W.js} +0 -0
  444. /package/dist/web/assets/{ini-DEE_vSHQ.js → ini-BInJO_T0.js} +0 -0
  445. /package/dist/web/assets/{java-CnmpcjcT.js → java-Dxuddnkb.js} +0 -0
  446. /package/dist/web/assets/{javascript-C-M7sa2e.js → javascript-CVamJfLY.js} +0 -0
  447. /package/dist/web/assets/{json-BjZ3dAk6.js → json-BLZ0ozYk.js} +0 -0
  448. /package/dist/web/assets/{json5-BQ2vpqjp.js → json5-BnXzmMKs.js} +0 -0
  449. /package/dist/web/assets/{jsonc-C5MjoWGx.js → jsonc-CSgkSga7.js} +0 -0
  450. /package/dist/web/assets/{jsonl-JvhrmQJI.js → jsonl-CY5W2nF2.js} +0 -0
  451. /package/dist/web/assets/{jsonnet-C5pdZVuo.js → jsonnet-CEJYH3sF.js} +0 -0
  452. /package/dist/web/assets/{jssm-Db17uybX.js → jssm-CsJc7Uhk.js} +0 -0
  453. /package/dist/web/assets/{jsx-tJp2Cyqu.js → jsx-jCiRaHU7.js} +0 -0
  454. /package/dist/web/assets/{kanagawa-dragon-BculT_Y1.js → kanagawa-dragon-DfoA_GxX.js} +0 -0
  455. /package/dist/web/assets/{kanagawa-lotus-yQgHUGwU.js → kanagawa-lotus-BOLYJpWd.js} +0 -0
  456. /package/dist/web/assets/{kanagawa-wave-CyGoWN4d.js → kanagawa-wave-DfKlbT1G.js} +0 -0
  457. /package/dist/web/assets/{kdl-Bn8R1REF.js → kdl-CmssIFTi.js} +0 -0
  458. /package/dist/web/assets/{kotlin-DWughK73.js → kotlin-BRsNgp6x.js} +0 -0
  459. /package/dist/web/assets/{kusto--serPb0x.js → kusto-T8X9jUab.js} +0 -0
  460. /package/dist/web/assets/{laserwave-DfAPJ1tt.js → laserwave-DkJMVJ8Z.js} +0 -0
  461. /package/dist/web/assets/{lean-UAm1HOKl.js → lean-C65KGVPq.js} +0 -0
  462. /package/dist/web/assets/{less-Xj6Ka0fF.js → less-exIPdDlf.js} +0 -0
  463. /package/dist/web/assets/{lib-DgNcCtj3.js → lib-mC1NZfuw.js} +0 -0
  464. /package/dist/web/assets/{libesm-CPh1Av0r.js → libesm-DSutUtl1.js} +0 -0
  465. /package/dist/web/assets/{light-plus-BfS7gPcj.js → light-plus-DiHUXAZt.js} +0 -0
  466. /package/dist/web/assets/{llvm-DvWiVwvU.js → llvm-CJBnEzFM.js} +0 -0
  467. /package/dist/web/assets/{log-Bo5z9bNz.js → log-DxMyUMuP.js} +0 -0
  468. /package/dist/web/assets/{logo-CrIjcMZT.js → logo-CC9NKzca.js} +0 -0
  469. /package/dist/web/assets/{luau-C9TsYwNt.js → luau-Bw10I6zf.js} +0 -0
  470. /package/dist/web/assets/{make-GYq4qVbx.js → make-C_6M90m6.js} +0 -0
  471. /package/dist/web/assets/{markdown-p5mNledS.js → markdown-bR9_R-LF.js} +0 -0
  472. /package/dist/web/assets/{material-theme-CWYOVxsP.js → material-theme-BUx2Qujn.js} +0 -0
  473. /package/dist/web/assets/{material-theme-darker-BDGty7YH.js → material-theme-darker-CRJrPgKm.js} +0 -0
  474. /package/dist/web/assets/{material-theme-lighter-JbgOgV3n.js → material-theme-lighter-CUAYppPa.js} +0 -0
  475. /package/dist/web/assets/{material-theme-ocean-EMOpLpa2.js → material-theme-ocean-DuINF1fg.js} +0 -0
  476. /package/dist/web/assets/{material-theme-palenight-CY5hgm7h.js → material-theme-palenight-W6_icGCj.js} +0 -0
  477. /package/dist/web/assets/{matlab-C-He_NTH.js → matlab-rfqn_b3z.js} +0 -0
  478. /package/dist/web/assets/{mdx-Cr2TR5fj.js → mdx-Bh1B_YOJ.js} +0 -0
  479. /package/dist/web/assets/{mermaid-BYc0-B0U.js → mermaid-Dxf6Ya2k.js} +0 -0
  480. /package/dist/web/assets/{min-dark-CMJwqJqB.js → min-dark-CivZ2L0h.js} +0 -0
  481. /package/dist/web/assets/{min-light-Cx6vFiEX.js → min-light-B1Ykqtma.js} +0 -0
  482. /package/dist/web/assets/{mipsasm-Bjer5HwK.js → mipsasm-DynHaM0d.js} +0 -0
  483. /package/dist/web/assets/{mojo-t90PR-Y2.js → mojo-Clw5PbGH.js} +0 -0
  484. /package/dist/web/assets/{monokai-dci_PdCm.js → monokai-DFIooFZT.js} +0 -0
  485. /package/dist/web/assets/{moonbit-AAzI-Hd-.js → moonbit-DnBSZWGM.js} +0 -0
  486. /package/dist/web/assets/{move-kwBhnhrG.js → move-CCcL2PfL.js} +0 -0
  487. /package/dist/web/assets/{narrat-CXww2YQL.js → narrat-BDarg5Lk.js} +0 -0
  488. /package/dist/web/assets/{nextflow-groovy-CJXsuXGB.js → nextflow-groovy-DrZOeCNW.js} +0 -0
  489. /package/dist/web/assets/{night-owl-_GD204Wr.js → night-owl-BnZZCK7v.js} +0 -0
  490. /package/dist/web/assets/{night-owl-light-DNgkw3kl.js → night-owl-light-CEwBsVHS.js} +0 -0
  491. /package/dist/web/assets/{nix-BgiorKFd.js → nix-amCzOPMR.js} +0 -0
  492. /package/dist/web/assets/{nord-CaBmcxFC.js → nord-BIs2BC6q.js} +0 -0
  493. /package/dist/web/assets/{nushell-DUh4UGx1.js → nushell-Y02fo4yw.js} +0 -0
  494. /package/dist/web/assets/{objective-c-Cl9KU86S.js → objective-c-7xOZtWW1.js} +0 -0
  495. /package/dist/web/assets/{objective-cpp-DVkIA8A0.js → objective-cpp-eVtjUvVB.js} +0 -0
  496. /package/dist/web/assets/{ocaml-vvoHTak0.js → ocaml-Bi6ZwqG4.js} +0 -0
  497. /package/dist/web/assets/{odin-Cyjw8woZ.js → odin-8xf1Y0S9.js} +0 -0
  498. /package/dist/web/assets/{one-dark-pro-B1cviILi.js → one-dark-pro-hVv-_y_b.js} +0 -0
  499. /package/dist/web/assets/{one-light-BPGUJeYI.js → one-light-CJJ3MtHt.js} +0 -0
  500. /package/dist/web/assets/{openscad-BLMdTUU8.js → openscad-B2HcywLk.js} +0 -0
  501. /package/dist/web/assets/{pascal-i5niy3e7.js → pascal-C7fllV_7.js} +0 -0
  502. /package/dist/web/assets/{pkl-CuOV7SsU.js → pkl-qvLC1IWP.js} +0 -0
  503. /package/dist/web/assets/{plastic-Bf_zE5KL.js → plastic-BT1wHZv2.js} +0 -0
  504. /package/dist/web/assets/{plsql-cfqEtMD3.js → plsql-1MsmdmJD.js} +0 -0
  505. /package/dist/web/assets/{plus-BM-Ty0fp.js → plus-BhMZqFsN.js} +0 -0
  506. /package/dist/web/assets/{po-CFJcWgjr.js → po-BigskytD.js} +0 -0
  507. /package/dist/web/assets/{poimandres-G5YoaIFf.js → poimandres-DuklniF7.js} +0 -0
  508. /package/dist/web/assets/{polar-rcjs3i9R.js → polar-e2pjN_sp.js} +0 -0
  509. /package/dist/web/assets/{postcss-CBKY1FTZ.js → postcss-DtAeYjWw.js} +0 -0
  510. /package/dist/web/assets/{powerquery-Db7SXLbO.js → powerquery-Cr2PtehI.js} +0 -0
  511. /package/dist/web/assets/{powershell-LFy3MUTP.js → powershell-BxFTGLJ4.js} +0 -0
  512. /package/dist/web/assets/{prisma-BE8GR1lH.js → prisma-COWJiwdZ.js} +0 -0
  513. /package/dist/web/assets/{prolog-DOn5lR1e.js → prolog-DFvGKRx6.js} +0 -0
  514. /package/dist/web/assets/{proto-wr-TpAK_.js → proto-CcK80lFt.js} +0 -0
  515. /package/dist/web/assets/{puppet-D2Dkv62u.js → puppet-BF60bZ4F.js} +0 -0
  516. /package/dist/web/assets/{purescript-DvgJgPdy.js → purescript-zL_69MzU.js} +0 -0
  517. /package/dist/web/assets/{python-C7TlkHhK.js → python-CIwKhfb_.js} +0 -0
  518. /package/dist/web/assets/{qmldir-CtZMwhcW.js → qmldir-CXNe04Lo.js} +0 -0
  519. /package/dist/web/assets/{qss-dNZ2btnl.js → qss-C8lAWe6G.js} +0 -0
  520. /package/dist/web/assets/{r-BlqnNkHS.js → r-CL5ZVL3_.js} +0 -0
  521. /package/dist/web/assets/{racket-CzRQrGc4.js → racket-BBfh0uh0.js} +0 -0
  522. /package/dist/web/assets/{raku-BwutWhBF.js → raku-o6E8R5Q3.js} +0 -0
  523. /package/dist/web/assets/{react-I_EwPIZi.js → react-CBTnT4sY.js} +0 -0
  524. /package/dist/web/assets/{red-BMXMywmL.js → red-GomRF2Xp.js} +0 -0
  525. /package/dist/web/assets/{reg-BqOKrIOw.js → reg-_ffDIvx9.js} +0 -0
  526. /package/dist/web/assets/{regexp-Bero1BN2.js → regexp-Bl5sF65W.js} +0 -0
  527. /package/dist/web/assets/{rel-BXKWIElA.js → rel-DgMdqDuj.js} +0 -0
  528. /package/dist/web/assets/{riscv-BbbBYJBo.js → riscv-8b3-Ye4X.js} +0 -0
  529. /package/dist/web/assets/{ron-BtVTWW4b.js → ron-DrmU1KHP.js} +0 -0
  530. /package/dist/web/assets/{rose-pine-hvkvQzvU.js → rose-pine-C_-E2tF1.js} +0 -0
  531. /package/dist/web/assets/{rose-pine-dawn-D7lwhUei.js → rose-pine-dawn-DEfhNlFH.js} +0 -0
  532. /package/dist/web/assets/{rose-pine-moon-CoHDmsIw.js → rose-pine-moon-BdvX3gdn.js} +0 -0
  533. /package/dist/web/assets/{rosmsg-CdaZFgI8.js → rosmsg-B6GhGpd5.js} +0 -0
  534. /package/dist/web/assets/{rust-Dy2qsphD.js → rust-DmC97M1-.js} +0 -0
  535. /package/dist/web/assets/{sass-Bz7NVxDb.js → sass-B0tJOHLM.js} +0 -0
  536. /package/dist/web/assets/{save-C4z1p51e.js → save-CtIGOjmx.js} +0 -0
  537. /package/dist/web/assets/{scala-sXSwsNyq.js → scala-n8zaFOXv.js} +0 -0
  538. /package/dist/web/assets/{scheme-DS7TUbN7.js → scheme-BKD9ztBU.js} +0 -0
  539. /package/dist/web/assets/{sdbl-iISFvIep.js → sdbl-D8gB5-rS.js} +0 -0
  540. /package/dist/web/assets/{shellscript-BkIl4ApC.js → shellscript-BfXlf_Ao.js} +0 -0
  541. /package/dist/web/assets/{shield-check-C5laObJv.js → shield-check-B0iGUiOK.js} +0 -0
  542. /package/dist/web/assets/{shield-off-DULFtyLS.js → shield-off-BNLWBhcq.js} +0 -0
  543. /package/dist/web/assets/{slack-dark-B1eg0_m2.js → slack-dark-CylEddse.js} +0 -0
  544. /package/dist/web/assets/{slack-ochin-mdkc1ldu.js → slack-ochin-w8wg8vuC.js} +0 -0
  545. /package/dist/web/assets/{smalltalk-CORtd03t.js → smalltalk-hiAXA64u.js} +0 -0
  546. /package/dist/web/assets/{snazzy-light-Bc6Q2G89.js → snazzy-light-BwlV3Xty.js} +0 -0
  547. /package/dist/web/assets/{solarized-dark-CF83zRXM.js → solarized-dark-vmNGdykz.js} +0 -0
  548. /package/dist/web/assets/{solarized-light-BoKQZaYU.js → solarized-light-DGv_0JXq.js} +0 -0
  549. /package/dist/web/assets/{solidity-DOgi1N4w.js → solidity-Y7QSH0B1.js} +0 -0
  550. /package/dist/web/assets/{sparkles-Dqsf3hq6.js → sparkles-Dh-_J2Rd.js} +0 -0
  551. /package/dist/web/assets/{splunk-BXfw5ZLy.js → splunk-BtgpLjHi.js} +0 -0
  552. /package/dist/web/assets/{sql-BcVs-eX4.js → sql-CRHYhOia.js} +0 -0
  553. /package/dist/web/assets/{ssh-config-CLCtNkkd.js → ssh-config-DOqSs3au.js} +0 -0
  554. /package/dist/web/assets/{stylus-DdP992aO.js → stylus-crCAEaBT.js} +0 -0
  555. /package/dist/web/assets/{swift-BnGb6Lgc.js → swift-eE3rT7TC.js} +0 -0
  556. /package/dist/web/assets/{synthwave-84-cg9264l9.js → synthwave-84-BWy14LBX.js} +0 -0
  557. /package/dist/web/assets/{system-verilog-GbHpszs3.js → system-verilog-DC0-DZM4.js} +0 -0
  558. /package/dist/web/assets/{systemd-Dz-bNsDQ.js → systemd-to5p6N8I.js} +0 -0
  559. /package/dist/web/assets/{table-CHmxuh6M.js → table-Bbc783ko.js} +0 -0
  560. /package/dist/web/assets/{talonscript-CFIidoBA.js → talonscript-DoTcU8wm.js} +0 -0
  561. /package/dist/web/assets/{tasl-Cu-TRTKx.js → tasl-cNZvUs95.js} +0 -0
  562. /package/dist/web/assets/{tcl-DwCHCB1K.js → tcl-mZE8sZH8.js} +0 -0
  563. /package/dist/web/assets/{terraform-BzZw4T1z.js → terraform-CqHPpIfu.js} +0 -0
  564. /package/dist/web/assets/{tokyo-night-RKsj2-tv.js → tokyo-night-Tp2rTIZd.js} +0 -0
  565. /package/dist/web/assets/{toml-CirbOgDP.js → toml-CU_vscgn.js} +0 -0
  566. /package/dist/web/assets/{tsv-CraRcWdU.js → tsv-ik7iFB9D.js} +0 -0
  567. /package/dist/web/assets/{tsx-CjFg0lgw.js → tsx-BMvvMKiv.js} +0 -0
  568. /package/dist/web/assets/{turtle-B-WBIOa6.js → turtle-DBqKCq9o.js} +0 -0
  569. /package/dist/web/assets/{typescript-MpLarYn1.js → typescript-DXJDmMZZ.js} +0 -0
  570. /package/dist/web/assets/{typespec-B7QlHMZ_.js → typespec-CBseCMtZ.js} +0 -0
  571. /package/dist/web/assets/{typst-CcHSKFZj.js → typst-4oY25U9y.js} +0 -0
  572. /package/dist/web/assets/{use-is-mobile-BXGVvczu.js → use-is-mobile-D1weanbj.js} +0 -0
  573. /package/dist/web/assets/{utils-DoY4yTP-.js → utils-BNRH507E.js} +0 -0
  574. /package/dist/web/assets/{v-xTGhWZuD.js → v-BXaZ_1oy.js} +0 -0
  575. /package/dist/web/assets/{vala-DwebjH3H.js → vala-BImWJE7z.js} +0 -0
  576. /package/dist/web/assets/{vb-BxJT2RAy.js → vb-CLfj2_sy.js} +0 -0
  577. /package/dist/web/assets/{vendor-xterm-CU6zIYMB.js → vendor-xterm-_yyu5n32.js} +0 -0
  578. /package/dist/web/assets/{verilog-CvV6RHPD.js → verilog-BWa8Ed2b.js} +0 -0
  579. /package/dist/web/assets/{vesper-CY3qd7E_.js → vesper-BGj1cg6m.js} +0 -0
  580. /package/dist/web/assets/{vhdl-B9ZX-sUL.js → vhdl-mByK7mlx.js} +0 -0
  581. /package/dist/web/assets/{viml-Y42h_ExQ.js → viml-CugmlW5g.js} +0 -0
  582. /package/dist/web/assets/{vitesse-black-DkXP6Sv-.js → vitesse-black-vPdpy9TL.js} +0 -0
  583. /package/dist/web/assets/{vitesse-dark-B9CkIB6r.js → vitesse-dark-BuQK3dUh.js} +0 -0
  584. /package/dist/web/assets/{vitesse-light-C7DHrJCK.js → vitesse-light-B3nz1XL9.js} +0 -0
  585. /package/dist/web/assets/{vyper-Cnd9s5j-.js → vyper-DH_GTIHG.js} +0 -0
  586. /package/dist/web/assets/{wasm-DQE71XTB.js → wasm-C-LF9GjX.js} +0 -0
  587. /package/dist/web/assets/{wasm-iWe7Fzjc.js → wasm-DfVbVJlW.js} +0 -0
  588. /package/dist/web/assets/{wenyan-Bc5Pnm9l.js → wenyan-CzQkHyHx.js} +0 -0
  589. /package/dist/web/assets/{wgsl-D-YUAWkP.js → wgsl-Du_M1DW9.js} +0 -0
  590. /package/dist/web/assets/{wikitext-DYQDUYf2.js → wikitext-BWN_w5S7.js} +0 -0
  591. /package/dist/web/assets/{wit-P6u7mfwy.js → wit-BR13v5HD.js} +0 -0
  592. /package/dist/web/assets/{wolfram-6SXLUqao.js → wolfram-BoXxp_Xl.js} +0 -0
  593. /package/dist/web/assets/{x-BcqfTY_7.js → x-nhriOX9o.js} +0 -0
  594. /package/dist/web/assets/{yaml-CQiMLVcZ.js → yaml-DpR_EF-d.js} +0 -0
  595. /package/dist/web/assets/{zenscript-DFMywo_P.js → zenscript-C-EdUtUd.js} +0 -0
  596. /package/dist/web/assets/{zig-CtAjaIiF.js → zig-BD4eA5nU.js} +0 -0
@@ -1,22 +1,27 @@
1
1
  /**
2
2
  * TabPool — persistent tab rendering with DOM reparenting.
3
3
  *
4
- * All tab components are mounted ONCE in a hidden off-screen container and
5
- * never unmounted when moved between panels or split. useLayoutEffect
6
- * physically moves each tab's wrapper DOM node into the correct panel slot
7
- * via appendChild (which moves, not clones). Component instances, hooks,
8
- * and all internal state (xterm buffer, Monaco editor, chat scroll) survive.
4
+ * All tab components are mounted ONCE and never unmounted when moved between panels or
5
+ * split. Each tab gets a wrapper element that is created once and portalled into; a
6
+ * layout effect physically moves that wrapper into the correct panel slot via appendChild
7
+ * (which moves, not clones). Component instances, hooks, and all internal state (xterm
8
+ * buffer, Monaco editor, chat scroll) survive.
9
9
  *
10
- * Why not createPortal? Changing a portal's container element causes React
11
- * to unmount/remount the children defeating the purpose.
10
+ * A portal is safe here precisely because the container element is stable: the objection
11
+ * to portals is that *changing* a portal's container remounts its children, and nothing
12
+ * ever swaps a wrapper. In exchange React binds its listeners to the wrapper itself, so
13
+ * they travel with the node — including into another document.
12
14
  */
13
- import { useRef, useLayoutEffect, useEffect, useSyncExternalStore, Suspense, lazy } from "react";
14
- import { Loader2 } from "lucide-react";
15
+ import { useState, useEffect, useSyncExternalStore, lazy } from "react";
15
16
  import { usePanelStore } from "@/stores/panel-store";
16
17
  import { useMountedTabsStore } from "@/stores/mounted-tabs-store";
17
18
  import type { TabType } from "@/stores/tab-store";
18
19
  import { collectTabEntries, filterMountableEntries } from "./tab-pool-collect";
19
- import { DOCK_PANEL_ID } from "@/stores/panel-utils";
20
+ import { slotRegistry } from "./tab-pool-registry";
21
+ import { ReparentingTab } from "./reparenting-tab";
22
+ import { useWindowPanelReconcile } from "./use-window-panel-reconcile";
23
+
24
+ export { registerPanelSlot } from "./tab-pool-registry";
20
25
 
21
26
  // ---------------------------------------------------------------------------
22
27
  // Lazy tab components (single source of truth for all tab types)
@@ -39,59 +44,29 @@ const TAB_COMPONENTS: Record<TabType, React.LazyExoticComponent<React.ComponentT
39
44
  group: lazy(() => import("@/components/group-chat/group-chat-tab").then((m) => ({ default: m.GroupChatTab }))),
40
45
  };
41
46
 
42
- // ---------------------------------------------------------------------------
43
- // Slot registry — panels register their content container refs here
44
- // ---------------------------------------------------------------------------
45
- type SlotListener = () => void;
46
-
47
- class SlotRegistry {
48
- private slots = new Map<string, HTMLDivElement>();
49
- private listeners = new Set<SlotListener>();
50
- private version = 0;
51
-
52
- register(panelId: string, el: HTMLDivElement | null) {
53
- if (el) {
54
- if (this.slots.get(panelId) === el) return;
55
- this.slots.set(panelId, el);
56
- } else {
57
- if (!this.slots.has(panelId)) return;
58
- this.slots.delete(panelId);
59
- }
60
- this.version++;
61
- this.listeners.forEach((fn) => fn());
62
- }
63
-
64
- get(panelId: string): HTMLDivElement | undefined {
65
- return this.slots.get(panelId);
66
- }
67
-
68
- subscribe(fn: SlotListener): () => void {
69
- this.listeners.add(fn);
70
- return () => this.listeners.delete(fn);
71
- }
72
-
73
- getVersion(): number {
74
- return this.version;
75
- }
76
- }
77
-
78
- const registry = new SlotRegistry();
79
-
80
- /** Called by EditorPanel to register its content slot */
81
- export function registerPanelSlot(panelId: string, el: HTMLDivElement | null) {
82
- registry.register(panelId, el);
83
- }
47
+ const HIDDEN_CONTAINER_STYLE: React.CSSProperties = {
48
+ position: "fixed",
49
+ top: 0,
50
+ left: 0,
51
+ width: 0,
52
+ height: 0,
53
+ overflow: "hidden",
54
+ pointerEvents: "none",
55
+ visibility: "hidden",
56
+ };
84
57
 
85
58
  // ---------------------------------------------------------------------------
86
- // TabPool — renders all tabs in a hidden container, reparents into slots
59
+ // TabPool — renders all tabs into wrappers parked off-screen, reparents into slots
87
60
  // ---------------------------------------------------------------------------
88
61
  export function TabPool() {
89
- const hiddenRef = useRef<HTMLDivElement>(null);
62
+ // State, not a ref: a tab's wrapper must be appended to a real element on its very
63
+ // first layout effect, and a parent ref is still null while its children's effects run.
64
+ const [hiddenContainer, setHiddenContainer] = useState<HTMLDivElement | null>(null);
90
65
 
91
66
  // Re-render when slots change (panel mount/unmount)
92
67
  useSyncExternalStore(
93
- (cb) => registry.subscribe(cb),
94
- () => registry.getVersion(),
68
+ (cb) => slotRegistry.subscribe(cb),
69
+ () => slotRegistry.getVersion(),
95
70
  );
96
71
 
97
72
  const panels = usePanelStore((s) => s.panels);
@@ -99,6 +74,10 @@ export function TabPool() {
99
74
  const currentProject = usePanelStore((s) => s.currentProject);
100
75
  const projectGrids = usePanelStore((s) => s.projectGrids);
101
76
 
77
+ // Detached tabs and their floating windows are persisted separately, so a reload can
78
+ // restore one without the other; this repairs the mismatch once per project load.
79
+ useWindowPanelReconcile();
80
+
102
81
  // Collect tabs from ALL mounted projects (not just active) to preserve
103
82
  // tab state across project switches (keep-alive). Each project's
104
83
  // PanelLayout stays mounted (CSS hidden), so slots remain registered.
@@ -133,118 +112,25 @@ export function TabPool() {
133
112
  }, [visibleKey]);
134
113
 
135
114
  return (
136
- // Off-screen mount point. React mounts tab wrappers here, then
137
- // useLayoutEffect moves them into panel slots before the browser paints.
138
- <div ref={hiddenRef} style={{ position: "fixed", top: 0, left: 0, width: 0, height: 0, overflow: "hidden", pointerEvents: "none", visibility: "hidden" }}>
139
- {tabEntries.map((entry) => (
140
- <ReparentingTab
141
- key={entry.tabId}
142
- tabId={entry.tabId}
143
- panelId={entry.panelId}
144
- type={entry.type}
145
- metadata={entry.metadata}
146
- isActive={entry.isActive}
147
- hiddenContainer={hiddenRef}
148
- />
149
- ))}
150
- </div>
151
- );
152
- }
153
-
154
- // ---------------------------------------------------------------------------
155
- // ReparentingTab — mounts once, physically moves between panel slots
156
- // ---------------------------------------------------------------------------
157
- interface ReparentingTabProps {
158
- tabId: string;
159
- panelId: string;
160
- type: TabType;
161
- metadata?: Record<string, unknown>;
162
- isActive: boolean;
163
- hiddenContainer: React.RefObject<HTMLDivElement | null>;
164
- }
165
-
166
- function ReparentingTab({ tabId, panelId, type, metadata, isActive, hiddenContainer }: ReparentingTabProps) {
167
- const wrapperRef = useRef<HTMLDivElement>(null);
168
- const Component = TAB_COMPONENTS[type];
169
-
170
- // On unmount: move wrapper back to hidden container so React's removeChild
171
- // can find it. Without this, the wrapper stays orphaned in the slot (the DOM
172
- // patch swallows the NotFoundError) and covers other tabs.
173
- useLayoutEffect(() => {
174
- return () => {
175
- const wrapper = wrapperRef.current;
176
- const hidden = hiddenContainer.current;
177
- if (wrapper && hidden && wrapper.parentElement !== hidden) {
178
- hidden.appendChild(wrapper);
179
- }
180
- };
181
- }, [hiddenContainer]);
182
-
183
- // Imperatively move the wrapper DOM node into the correct panel slot.
184
- // appendChild on an already-mounted node moves it (DOM spec — no clone/destroy).
185
- // useLayoutEffect runs before paint, so the user never sees the off-screen state.
186
- // No deps — must run every render because React's reconciliation may call
187
- // insertBefore() to reorder keyed children, moving reparented nodes back
188
- // to the hidden container. The early-return guard keeps this cheap.
189
- useLayoutEffect(() => {
190
- const wrapper = wrapperRef.current;
191
- const slot = registry.get(panelId);
192
- if (!wrapper) return;
193
-
194
- // Panel slot not mounted (e.g., mobile renders only the focused panel).
195
- // Move wrapper back to hidden container so it doesn't overlap in the wrong slot.
196
- if (!slot) {
197
- const hidden = hiddenContainer.current;
198
- if (hidden && wrapper.parentElement !== hidden) {
199
- hidden.appendChild(wrapper);
200
- }
201
- return;
202
- }
203
-
204
- if (wrapper.parentElement === slot) return;
205
-
206
- // Save scroll positions — appendChild resets them during the DOM move
207
- const scrollables: { el: Element; top: number; left: number }[] = [];
208
- wrapper.querySelectorAll("*").forEach((el) => {
209
- if (el.scrollTop || el.scrollLeft) {
210
- scrollables.push({ el, top: el.scrollTop, left: el.scrollLeft });
211
- }
212
- });
213
-
214
- slot.appendChild(wrapper);
215
-
216
- // Restore scroll positions synchronously before paint
217
- for (const { el, top, left } of scrollables) {
218
- el.scrollTop = top;
219
- el.scrollLeft = left;
220
- }
221
- });
222
-
223
- if (!Component) return null;
224
-
225
- return (
226
- <div
227
- ref={wrapperRef}
228
- className="absolute inset-0"
229
- style={isActive ? undefined : { display: "none" }}
230
- data-tab-pool-id={tabId}
231
- onMouseDownCapture={() => {
232
- // Tab content is DOM-reparented out of EditorPanel's fiber subtree, so
233
- // EditorPanel's onMouseDown never sees clicks on the content body. Set
234
- // focus here where panelId is known — keeps focusedPanelId correct for
235
- // every tab type. Dock content is excluded so new tabs open in the grid.
236
- if (panelId !== DOCK_PANEL_ID) usePanelStore.getState().setFocusedPanel(panelId);
237
- }}
238
- >
239
- <Suspense
240
- fallback={
241
- <div className="flex items-center justify-center h-full">
242
- <Loader2 className="size-6 animate-spin text-primary" />
243
- </div>
244
- }
245
- >
246
- <Component metadata={metadata} tabId={tabId} />
247
- </Suspense>
115
+ // Off-screen parking spot. Tab wrappers live here until a layout effect moves them
116
+ // into a panel slot, and come back whenever their panel has no slot mounted.
117
+ <div ref={setHiddenContainer} style={HIDDEN_CONTAINER_STYLE}>
118
+ {hiddenContainer &&
119
+ tabEntries.map((entry) => {
120
+ const Component = TAB_COMPONENTS[entry.type];
121
+ if (!Component) return null;
122
+ return (
123
+ <ReparentingTab
124
+ key={entry.tabId}
125
+ tabId={entry.tabId}
126
+ panelId={entry.panelId}
127
+ component={Component}
128
+ metadata={entry.metadata}
129
+ isActive={entry.isActive}
130
+ hiddenContainer={hiddenContainer}
131
+ />
132
+ );
133
+ })}
248
134
  </div>
249
135
  );
250
136
  }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * "Open in window" — detaches a tab into a floating window, keeping it live.
3
+ *
4
+ * Desktop only: the window layer renders nothing below the md breakpoint, so on a phone
5
+ * the item would move a tab somewhere the user cannot see (a scaled-down window is never
6
+ * the answer — see the mobile-first UI rules).
7
+ */
8
+ import { ExternalLink } from "lucide-react";
9
+ import { toast } from "sonner";
10
+ import { ContextMenuItem, ContextMenuSeparator } from "@/components/ui/context-menu";
11
+ import { useIsMobile } from "@/hooks/use-is-mobile";
12
+ import { usePanelStore } from "@/stores/panel-store";
13
+ import type { Tab } from "@/stores/tab-store";
14
+
15
+ interface TabPopOutMenuItemProps {
16
+ tab: Tab;
17
+ /** Panel the tab currently lives in — also where it re-docks when the window closes. */
18
+ panelId: string;
19
+ }
20
+
21
+ export function TabPopOutMenuItem({ tab, panelId }: TabPopOutMenuItemProps) {
22
+ const isMobile = useIsMobile();
23
+ // The monitor already has a window kind of its own; detaching it would open a second,
24
+ // duplicate presentation of the same data.
25
+ if (isMobile || tab.type === "system-monitor") return null;
26
+
27
+ return (
28
+ <>
29
+ <ContextMenuItem
30
+ onClick={() => {
31
+ const windowId = usePanelStore.getState().popOutTab(tab.id, panelId);
32
+ // The only rejection a desktop user can hit is the shared window cap.
33
+ if (!windowId) toast.error("Too many windows open — close one first");
34
+ }}
35
+ >
36
+ <ExternalLink className="size-3.5 mr-2" />
37
+ Open in window
38
+ </ContextMenuItem>
39
+ <ContextMenuSeparator />
40
+ </>
41
+ );
42
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * One-shot repair of the split between detached tabs and their floating windows.
3
+ *
4
+ * The two halves persist to different keys — window geometry is global and restored by
5
+ * the window layer, panels are restored with the project — so a reload can bring back one
6
+ * without the other. Runs once per project load, after the window layer has restored
7
+ * (otherwise every panel would look orphaned), and unconditionally on mobile, where the
8
+ * window layer renders nothing at all and a workspace synced from a desktop would
9
+ * otherwise hide its detached tabs with no way to reach them.
10
+ */
11
+ import { useEffect, useRef } from "react";
12
+ import { useIsMobile } from "@/hooks/use-is-mobile";
13
+ import { usePanelStore } from "@/stores/panel-store";
14
+ import { persistWindowPanelChange } from "@/stores/window-panel-actions";
15
+ import { reconcileRunKey, reconcileTabHostWindows } from "@/stores/window-panel-reconcile";
16
+ import { useWindowStore } from "@/components/floating-window/window-store";
17
+
18
+ export function useWindowPanelReconcile(): void {
19
+ const isMobile = useIsMobile();
20
+ const currentProject = usePanelStore((s) => s.currentProject);
21
+ const restored = useWindowStore((s) => s.restored);
22
+ const reconciledFor = useRef<string | null>(null);
23
+
24
+ useEffect(() => {
25
+ if (!currentProject) return;
26
+ if (!restored && !isMobile) return;
27
+ const runKey = reconcileRunKey(currentProject, isMobile);
28
+ if (reconciledFor.current === runKey) return;
29
+ reconciledFor.current = runKey;
30
+
31
+ const { panels, grid, focusedPanelId } = usePanelStore.getState();
32
+ // No window layer on mobile ⇒ no window is live ⇒ every detached tab comes home.
33
+ const liveWindowIds = isMobile
34
+ ? []
35
+ : Object.values(useWindowStore.getState().windows)
36
+ .filter((w) => w.kind === "tab-host")
37
+ .map((w) => w.id);
38
+
39
+ const result = reconcileTabHostWindows({ panels, grid, focusedPanelId, liveWindowIds });
40
+ if (result.changed) {
41
+ usePanelStore.setState({ panels: result.panels, focusedPanelId: result.focusedPanelId });
42
+ // Both halves: the window-panel blob shrank, and the tabs landed in a grid panel
43
+ // that belongs to the active project's layout.
44
+ persistWindowPanelChange(usePanelStore.getState);
45
+ }
46
+ for (const windowId of result.windowIdsToClose) useWindowStore.getState().close(windowId);
47
+ }, [currentProject, restored, isMobile]);
48
+ }
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * Finder-flavoured unified titlebar: traffic lights left (grey when unfocused, hover
3
- * reveals the glyph), title centred. Height matches the shared `TITLEBAR_HEIGHT` contract
4
- * constant (not the real Finder 38px) so a minimised window's collapsed height agrees with
5
- * what `FloatingWindow` reserves for it. The toolbar row (back/forward/view-switch/search)
6
- * is the existing `ExplorerToolbar` in the window body, tinted by `skins.css` — the chrome
7
- * slot is only the titlebar itself.
3
+ * reveals the glyph), title centred, picture-in-picture at the right end. Height matches the
4
+ * shared `TITLEBAR_HEIGHT` contract constant (not the real Finder 38px) so a minimised
5
+ * window's collapsed height agrees with what `FloatingWindow` reserves for it.
6
+ *
7
+ * Worn by EVERY window kind, not just the explorer: the frame resolves one skin for all of
8
+ * them. Anything below the titlebar (the explorer's own toolbar row, tinted by `skins.css`)
9
+ * belongs to the window body — the chrome slot is only the titlebar itself.
8
10
  *
9
11
  * `data-skin` is set here (not only on the explorer body) because this titlebar is a
10
12
  * sibling of the body in the window tree — the `--x-*` vars it reads only resolve on an
@@ -14,6 +16,7 @@
14
16
  import { Minus, Plus, X } from "lucide-react";
15
17
  import { cn } from "@/lib/utils";
16
18
  import { TITLEBAR_HEIGHT, type WindowChromeProps } from "@/components/floating-window/window-chrome-contract";
19
+ import { PipCaptionButton } from "@/components/floating-window/pip/pip-caption-button";
17
20
 
18
21
  /** The traffic-light colours, plus the unfocused grey — the documented hardcoded hexes. */
19
22
  const LIGHTS = [
@@ -23,7 +26,7 @@ const LIGHTS = [
23
26
  ] as const;
24
27
 
25
28
  export function MacosWindowChrome({
26
- title, focused, titlebarProps, onMinimize, onToggleMaximize, onClose,
29
+ id, title, focused, titlebarProps, onMinimize, onToggleMaximize, onClose,
27
30
  }: WindowChromeProps) {
28
31
  const { className, style, ...rest } = titlebarProps;
29
32
  const actions = [onClose, onMinimize, onToggleMaximize];
@@ -56,14 +59,20 @@ export function MacosWindowChrome({
56
59
  </button>
57
60
  ))}
58
61
  </div>
62
+ {/* A flex child, not an absolutely centred span: the title is boxed between the
63
+ traffic lights and the caption button, so a long title truncates instead of
64
+ painting over either of them. Centre is a few px off true middle — acceptable. */}
59
65
  <span
60
66
  className={cn(
61
- "pointer-events-none absolute inset-x-0 truncate text-center text-[13px]",
67
+ "pointer-events-none flex-1 min-w-0 truncate text-center text-[13px] px-2",
62
68
  focused ? "text-text" : "text-text-2",
63
69
  )}
64
70
  >
65
71
  {title}
66
72
  </span>
73
+ <div className="flex items-center pr-2">
74
+ <PipCaptionButton id={id} />
75
+ </div>
67
76
  </div>
68
77
  );
69
78
  }
@@ -1,10 +1,14 @@
1
1
  /**
2
- * Windows 11 File Explorer-flavoured titlebar: window icon + title left, caption buttons
3
- * right (46px wide, close hover red). Height matches the shared `TITLEBAR_HEIGHT` contract
4
- * constant (not the real Win11 32px) so a minimised window's collapsed height agrees with
5
- * what `FloatingWindow` reserves for it. The toolbar row (back/forward/breadcrumb/search) is
6
- * the existing `ExplorerToolbar` in the window body, tinted by `skins.css` — the chrome slot
7
- * is only the titlebar itself.
2
+ * Windows 11 File Explorer-flavoured titlebar: title left, caption buttons right (46px wide,
3
+ * close hover red), picture-in-picture left of minimize. Height matches the shared
4
+ * `TITLEBAR_HEIGHT` contract constant (not the real Win11 32px) so a minimised window's
5
+ * collapsed height agrees with what `FloatingWindow` reserves for it.
6
+ *
7
+ * Worn by EVERY window kind, not just the explorer: the frame resolves one skin for all of
8
+ * them. Hence the folder glyph is drawn only for `kind === "explorer"` — it is the File
9
+ * Explorer's app icon, and a system monitor wearing it would be a lie. Anything below the
10
+ * titlebar (the explorer's own toolbar row, tinted by `skins.css`) belongs to the window
11
+ * body — the chrome slot is only the titlebar itself.
8
12
  *
9
13
  * `data-skin` is set here (not only on the explorer body) because this titlebar is a
10
14
  * sibling of the body in the window tree — the `--x-*` vars it reads (`--x-titlebar-bg`,
@@ -14,6 +18,7 @@
14
18
  import { Minus, Square, Copy, X } from "lucide-react";
15
19
  import { cn } from "@/lib/utils";
16
20
  import { TITLEBAR_HEIGHT, type WindowChromeProps } from "@/components/floating-window/window-chrome-contract";
21
+ import { PipCaptionButton } from "@/components/floating-window/pip/pip-caption-button";
17
22
  import { WindowsFolderIcon } from "./folder-icon-windows";
18
23
 
19
24
  /** Windows metric: 46px wide caption buttons, full titlebar height. */
@@ -23,7 +28,7 @@ const CAPTION_BUTTON =
23
28
  const CLOSE_BUTTON = cn(CAPTION_BUTTON, "can-hover:hover:!bg-[#c42b1c] can-hover:hover:!text-white");
24
29
 
25
30
  export function WindowsWindowChrome({
26
- title, state, focused, titlebarProps, onMinimize, onToggleMaximize, onClose,
31
+ id, kind, title, state, focused, titlebarProps, onMinimize, onToggleMaximize, onClose,
27
32
  }: WindowChromeProps) {
28
33
  const { className, style, ...rest } = titlebarProps;
29
34
  return (
@@ -39,9 +44,10 @@ export function WindowsWindowChrome({
39
44
  )}
40
45
  >
41
46
  <div className="flex flex-1 min-w-0 items-center gap-2 px-2.5 cursor-default">
42
- <WindowsFolderIcon className="size-4 shrink-0" />
47
+ {kind === "explorer" && <WindowsFolderIcon className="size-4 shrink-0" />}
43
48
  <span className={cn("truncate text-[12px]", focused ? "text-text" : "text-text-2")}>{title}</span>
44
49
  </div>
50
+ <PipCaptionButton id={id} className={CAPTION_BUTTON} />
45
51
  <button type="button" aria-label="Minimize window" className={CAPTION_BUTTON} onClick={onMinimize}>
46
52
  <Minus className="size-3.5" />
47
53
  </button>
@@ -1,4 +1,9 @@
1
- import type { KillProcessRequest, ProcessInfo } from "../../../types/system-metrics";
1
+ import type { KillProcessRequest, ProcessGroup, ProcessInfo } from "../../../types/system-metrics";
2
+
3
+ /** What the confirm dialog is about to end: one row, or a whole app group. */
4
+ export type KillTarget =
5
+ | { kind: "process"; proc: ProcessInfo }
6
+ | { kind: "group"; group: ProcessGroup; members: ProcessInfo[] };
2
7
 
3
8
  /** Builds the kill request body from a process row's own identity fields — pulled out
4
9
  * of the dialog's confirm handler so the payload shape is unit-testable without
@@ -7,3 +12,25 @@ import type { KillProcessRequest, ProcessInfo } from "../../../types/system-metr
7
12
  export function buildKillRequest(proc: ProcessInfo, tree: boolean): KillProcessRequest {
8
13
  return { pid: proc.pid, startedAt: proc.startedAt, tree };
9
14
  }
15
+
16
+ /**
17
+ * Requests that end every member of a group.
18
+ *
19
+ * An ancestor roll-up ("root:<pid>") is ended with ONE tree kill on its root — the
20
+ * server walks the same descendant set it grouped by, so this is atomic on Windows
21
+ * (`taskkill /T`) and cannot miss a child that forked between snapshot and click.
22
+ * An orphan bucket ("exe:<name>") has no common ancestor, so each member is a
23
+ * separate single-process kill. Protected members are never included: the caller
24
+ * disables the button when any exist, and the server would 403 anyway.
25
+ */
26
+ export function buildGroupKillRequests(group: ProcessGroup, members: ProcessInfo[]): KillProcessRequest[] {
27
+ const root = group.rootPid !== null ? members.find((m) => m.pid === group.rootPid) : undefined;
28
+ if (root) return [buildKillRequest(root, true)];
29
+ return members.map((m) => buildKillRequest(m, false));
30
+ }
31
+
32
+ /** A group can be ended only when none of its members is refused by the guard —
33
+ * the PPM instance and system groups stay un-endable as a whole. */
34
+ export function isGroupProtected(members: ProcessInfo[]): boolean {
35
+ return members.some((m) => m.protected);
36
+ }
@@ -1,8 +1,8 @@
1
1
  /** Shared scale resolution for the canvas charts: a fixed ceiling (percentage axes)
2
2
  * or autoscale to the max of the visible window (byte-rate axes) — pulled out of
3
- * `sparkline-canvas.tsx`/`metric-chart-canvas.tsx` so it is unit-testable without a
4
- * canvas context. This is the fix for the bug where a 3% CPU reading rendered as a
5
- * full-height sparkline: the old code always did `Math.max(...data, 1)`. */
3
+ * `metric-chart-canvas.tsx` so it is unit-testable without a canvas context. A
4
+ * percentage axis must be pinned to 100, otherwise a 3% CPU reading autoscales to a
5
+ * full-height line. */
6
6
  export function resolveScaleMax(data: number[], maxValue?: number): number {
7
7
  if (maxValue !== undefined) return maxValue;
8
8
  return Math.max(...data, 1);
@@ -5,44 +5,60 @@ import { Button } from "@/components/ui/button";
5
5
  import { Switch } from "@/components/ui/switch";
6
6
  import { useIsMobile } from "@/hooks/use-is-mobile";
7
7
  import { formatRam } from "@/lib/format-bytes";
8
- import type { ProcessInfo } from "../../../types/system-metrics";
8
+ import type { KillTarget } from "./build-kill-request";
9
9
 
10
10
  export interface KillConfirmDialogProps {
11
- process: ProcessInfo | null;
12
- onConfirm: (proc: ProcessInfo, tree: boolean) => void;
11
+ target: KillTarget | null;
12
+ /** `tree` is only meaningful for a single process; a group is always ended whole. */
13
+ onConfirm: (target: KillTarget, tree: boolean) => void;
13
14
  onCancel: () => void;
14
15
  }
15
16
 
16
- /** Confirm dialog for ending a process. Bottom sheet below `md`, centered dialog at
17
- * `md`+. The "end child processes" toggle defaults off and the destructive action is
18
- * never the default focus — Escape/backdrop cancels either way. */
19
- export function KillConfirmDialog({ process, onConfirm, onCancel }: KillConfirmDialogProps) {
17
+ /** Confirm dialog for ending a process or a whole app group. Bottom sheet below
18
+ * `md`, centered dialog at `md`+. The "end child processes" toggle defaults off and
19
+ * the destructive action is never the default focus — Escape/backdrop cancels either
20
+ * way. */
21
+ export function KillConfirmDialog({ target, onConfirm, onCancel }: KillConfirmDialogProps) {
20
22
  const isMobile = useIsMobile();
21
23
  const [tree, setTree] = useState(false);
22
24
 
23
25
  useEffect(() => {
24
- if (!process) setTree(false);
25
- }, [process]);
26
+ if (!target) setTree(false);
27
+ }, [target]);
26
28
 
27
- if (!process) return null;
29
+ if (!target) return null;
30
+
31
+ const isGroup = target.kind === "group";
32
+ const title = isGroup ? "End app" : "End process";
33
+ const cpu = isGroup ? target.group.cpu : target.proc.cpu;
34
+ const ramMB = isGroup ? target.group.ramMB : target.proc.ramMB;
28
35
 
29
36
  const body = (
30
- <div className="space-y-4" data-testid="sysmon-kill-confirm">
31
- <p className="text-sm">
32
- End <span className="font-medium">{process.name}</span> (pid {process.pid})?
33
- </p>
37
+ <div className="space-y-4" data-testid="sysmon-kill-confirm" data-kill-kind={target.kind}>
38
+ {isGroup ? (
39
+ <p className="text-sm">
40
+ End <span className="font-medium">{target.group.label}</span> and all its{" "}
41
+ <span className="font-medium">{target.members.length}</span> processes?
42
+ </p>
43
+ ) : (
44
+ <p className="text-sm">
45
+ End <span className="font-medium">{target.proc.name}</span> (pid {target.proc.pid})?
46
+ </p>
47
+ )}
34
48
  <p className="text-xs text-text-subtle">
35
- CPU {process.cpu.toFixed(1)}% · RAM {formatRam(process.ramMB)}
49
+ CPU {cpu.toFixed(1)}% · RAM {formatRam(ramMB)}
36
50
  </p>
37
- <label className="flex items-center justify-between gap-3 min-h-11 py-1">
38
- <span className="text-sm">Also end child processes</span>
39
- <Switch
40
- checked={tree}
41
- onCheckedChange={setTree}
42
- data-testid="sysmon-kill-tree"
43
- aria-label="Also end child processes"
44
- />
45
- </label>
51
+ {!isGroup && (
52
+ <label className="flex items-center justify-between gap-3 min-h-11 py-1">
53
+ <span className="text-sm">Also end child processes</span>
54
+ <Switch
55
+ checked={tree}
56
+ onCheckedChange={setTree}
57
+ data-testid="sysmon-kill-tree"
58
+ aria-label="Also end child processes"
59
+ />
60
+ </label>
61
+ )}
46
62
  <div className="flex flex-col-reverse md:flex-row gap-2 md:justify-end pt-2">
47
63
  <Button
48
64
  variant="outline"
@@ -55,11 +71,11 @@ export function KillConfirmDialog({ process, onConfirm, onCancel }: KillConfirmD
55
71
  <Button
56
72
  variant="destructive"
57
73
  autoFocus={false}
58
- onClick={() => onConfirm(process, tree)}
74
+ onClick={() => onConfirm(target, tree)}
59
75
  data-testid="sysmon-kill-confirm-ok"
60
76
  className="min-h-11"
61
77
  >
62
- End process
78
+ {isGroup ? `End ${target.members.length} processes` : "End process"}
63
79
  </Button>
64
80
  </div>
65
81
  </div>
@@ -69,7 +85,7 @@ export function KillConfirmDialog({ process, onConfirm, onCancel }: KillConfirmD
69
85
  return (
70
86
  <BottomSheet open onClose={onCancel}>
71
87
  <div className="px-4 pb-4">
72
- <h2 className="text-base font-semibold mb-3">End process</h2>
88
+ <h2 className="text-base font-semibold mb-3">{title}</h2>
73
89
  {body}
74
90
  </div>
75
91
  </BottomSheet>
@@ -80,7 +96,7 @@ export function KillConfirmDialog({ process, onConfirm, onCancel }: KillConfirmD
80
96
  <Dialog open onOpenChange={(open) => !open && onCancel()}>
81
97
  <DialogContent>
82
98
  <DialogHeader>
83
- <DialogTitle>End process</DialogTitle>
99
+ <DialogTitle>{title}</DialogTitle>
84
100
  </DialogHeader>
85
101
  {body}
86
102
  </DialogContent>
@@ -20,8 +20,7 @@ interface MetricChartCanvasProps {
20
20
  * width of its parent — the cards live in a resizable floating window, so a fixed
21
21
  * pixel width overflows the card as soon as the window is narrower than designed.
22
22
  * No redraw throttle — one draw per prop change, which at a 2s tick cadence is
23
- * nowhere near a frame budget. `SparklineCanvas` stays the small inline variant
24
- * used by process rows. */
23
+ * nowhere near a frame budget. */
25
24
  export const MetricChartCanvas = memo(function MetricChartCanvas({
26
25
  series,
27
26
  height,