@hienlh/ppm 0.18.10 → 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 (575) hide show
  1. package/.spike/cdp-client.ts +116 -0
  2. package/.spike/cleanup-ppm-workspace.mjs +44 -0
  3. package/.spike/pip-eval.ts +17 -0
  4. package/.spike/pip-page-helpers.js +119 -0
  5. package/.spike/pip-results-editor-glue.json +105 -0
  6. package/.spike/pip-results-editor.json +158 -0
  7. package/.spike/pip-results-input-isolation.json +28 -0
  8. package/.spike/pip-results-key-probe.json +58 -0
  9. package/.spike/pip-results-listener-survival.json +98 -0
  10. package/.spike/pip-results-listeners.json +36 -0
  11. package/.spike/pip-results-pagehide.json +92 -0
  12. package/.spike/pip-results-resize.json +104 -0
  13. package/.spike/pip-results-terminal-debug.json +99 -0
  14. package/.spike/pip-results-terminal-v2.json +191 -0
  15. package/.spike/pip-results-terminal.json +220 -0
  16. package/.spike/pip-results-xterm-trace.json +73 -0
  17. package/.spike/pip-spike-common.ts +83 -0
  18. package/.spike/pip-spike-editor-glue.ts +82 -0
  19. package/.spike/pip-spike-editor.ts +129 -0
  20. package/.spike/pip-spike-input-isolation.ts +38 -0
  21. package/.spike/pip-spike-key-probe.ts +34 -0
  22. package/.spike/pip-spike-listener-survival.ts +54 -0
  23. package/.spike/pip-spike-listeners.ts +19 -0
  24. package/.spike/pip-spike-pagehide.ts +59 -0
  25. package/.spike/pip-spike-resize.ts +42 -0
  26. package/.spike/pip-spike-setup.ts +63 -0
  27. package/.spike/pip-spike-terminal-debug.ts +81 -0
  28. package/.spike/pip-spike-terminal-v2.ts +139 -0
  29. package/.spike/pip-spike-terminal.ts +169 -0
  30. package/.spike/pip-spike-xterm-trace.ts +36 -0
  31. package/.spike/probe-drag-memo-error.mjs +67 -0
  32. package/.spike/read-dev-db.ts +15 -0
  33. package/.spike/scratch-editor-target.ts +7 -0
  34. package/.spike/smoke-unified-chrome-pip.mjs +353 -0
  35. package/.spike/tunnel.err +41 -0
  36. package/.spike/vite-restart.err +663 -0
  37. package/.spike/w.err +543 -0
  38. package/CHANGELOG.md +13 -0
  39. package/assets/skills/ppm/SKILL.md +1 -1
  40. package/assets/skills/ppm/references/http-api.md +1 -1
  41. package/dist/web/assets/adaptive-context-menu-DGCKKeTy.js +1 -0
  42. package/dist/web/assets/ai-resource-editor-BOHHTTe7.js +1 -0
  43. package/dist/web/assets/ai-settings-section-BigC_nZR.js +1 -0
  44. package/dist/web/assets/{angular-html-Ctz0zN01.js → angular-html-BYPaydvh.js} +1 -1
  45. package/dist/web/assets/{angular-ts-QXHpgB1C.js → angular-ts-Bb4AMKlk.js} +1 -1
  46. package/dist/web/assets/api-client-DTZEgjBQ.js +1 -0
  47. package/dist/web/assets/{api-settings-kQHV6pYZ.js → api-settings-Bd3M4YpI.js} +1 -1
  48. package/dist/web/assets/api-settings-kd1j4iR8.js +1 -0
  49. package/dist/web/assets/{api-themes-CrK0tkW_.js → api-themes-Cpx419SL.js} +1 -1
  50. package/dist/web/assets/{apl-b9KVk-O4.js → apl-DB2VjFCS.js} +1 -1
  51. package/dist/web/assets/architecture-PBZL5I3N-8dCMT9r7.js +1 -0
  52. package/dist/web/assets/{astro-BlFEeGYr.js → astro-BeoJdVpk.js} +1 -1
  53. package/dist/web/assets/{audio-preview-CZLmhf__.js → audio-preview-BgFuTn-0.js} +1 -1
  54. package/dist/web/assets/{blade-CGkj2MUI.js → blade-Cf2q1sPf.js} +1 -1
  55. package/dist/web/assets/{bsl-CVsbb6ap.js → bsl-W1kOaNpu.js} +1 -1
  56. package/dist/web/assets/{button-BA_fJOtX.js → button-5zTrkEoN.js} +1 -1
  57. package/dist/web/assets/{cairo-BbIHl2iE.js → cairo-BoAZFcje.js} +1 -1
  58. package/dist/web/assets/chat-tab-E3bf_V8X.js +15 -0
  59. package/dist/web/assets/chevron-down-Dp0rpHb8.js +1 -0
  60. package/dist/web/assets/clipboard-7fw0Shic.js +1 -0
  61. package/dist/web/assets/{cobol-BWSGjmWb.js → cobol-B0Nm7RXj.js} +1 -1
  62. package/dist/web/assets/code-editor-BUlM2gXk.js +10 -0
  63. package/dist/web/assets/{coffee-CWGNOxds.js → coffee-BakMn3on.js} +1 -1
  64. package/dist/web/assets/{conflict-editor-fRTU3f1o.js → conflict-editor-BUs5k9q_.js} +4 -4
  65. package/dist/web/assets/context-menu-Bq92km4s.js +1 -0
  66. package/dist/web/assets/{cpp-DHGV24J2.js → cpp-BRAJQHK2.js} +1 -1
  67. package/dist/web/assets/{crystal-Vlwf2UMP.js → crystal-kqqO3zkK.js} +1 -1
  68. package/dist/web/assets/{csv-preview-D1d0cxOJ.js → csv-preview-ByoDgLAZ.js} +1 -1
  69. package/dist/web/assets/{data-grid-overlay-editor-D9il5DZS.js → data-grid-overlay-editor-Brkf9z41.js} +1 -1
  70. package/dist/web/assets/database-viewer-Btc3h8Yu.js +1 -0
  71. package/dist/web/assets/dialog-DwtGlowE.js +1 -0
  72. package/dist/web/assets/diff-viewer-CZaDnR4w.js +4 -0
  73. package/dist/web/assets/docx-preview-DAD_vg59.js +12 -0
  74. package/dist/web/assets/{edge-Bhqk64zS.js → edge-Cq1ZKZuE.js} +1 -1
  75. package/dist/web/assets/{edit-diff-preview-gq6ALf2B.js → edit-diff-preview-B_VE7ppQ.js} +1 -1
  76. package/dist/web/assets/{elixir-D466WCFm.js → elixir-BM_JoJRk.js} +1 -1
  77. package/dist/web/assets/{elm-Bfs2qNPR.js → elm-Dw7oO08Y.js} +1 -1
  78. package/dist/web/assets/{erb-WI7jUaB6.js → erb-CfKOGAfk.js} +1 -1
  79. package/dist/web/assets/{erlang-BCBe8nCc.js → erlang-DlDmGVeE.js} +1 -1
  80. package/dist/web/assets/{esm-CZs9WVDO.js → esm-DGAP0GFh.js} +1 -1
  81. package/dist/web/assets/explorer-body-9aBmyVpZ.js +2 -0
  82. package/dist/web/assets/explorer-window-content-DRtqoSPA.js +1 -0
  83. package/dist/web/assets/extension-store-Cpfh2vMm.js +1 -0
  84. package/dist/web/assets/{extension-webview-BH_mWYMe.js → extension-webview-B2OnjuCu.js} +2 -2
  85. package/dist/web/assets/external-link-B8BynhvY.js +1 -0
  86. package/dist/web/assets/file-store-aOERIEo-.js +1 -0
  87. package/dist/web/assets/folder-symlink-B8XrtvQK.js +1 -0
  88. package/dist/web/assets/{fortran-fixed-form-CXLIv8Ox.js → fortran-fixed-form-XnAhQWxS.js} +1 -1
  89. package/dist/web/assets/{fsharp-CZsYmiw9.js → fsharp-CdNvy0PG.js} +1 -1
  90. package/dist/web/assets/{gdresource-BGRvoWfC.js → gdresource-DxuG5i8i.js} +1 -1
  91. package/dist/web/assets/{git-commit-DH6mY3Km.js → git-commit-3lMonsur.js} +1 -1
  92. package/dist/web/assets/git-log-panel-Co0PBPHC.js +1 -0
  93. package/dist/web/assets/{git-rebase-C5Gql6Ma.js → git-rebase-N8Z9kIMf.js} +1 -1
  94. package/dist/web/assets/gitGraph-HDMCJU4V-2e_8dfbs.js +1 -0
  95. package/dist/web/assets/{glide-data-grid-n4ox_uev.js → glide-data-grid-C4_zIneN.js} +8 -8
  96. package/dist/web/assets/{glimmer-js-1-6JMJlj.js → glimmer-js-Dp9OXiib.js} +1 -1
  97. package/dist/web/assets/{glimmer-ts-C_Njj0nK.js → glimmer-ts-CDVKBcDj.js} +1 -1
  98. package/dist/web/assets/{glsl-DbkxE1TN.js → glsl-DPJxI4us.js} +1 -1
  99. package/dist/web/assets/{graphql-BK75fiXl.js → graphql-BrFhaQQl.js} +1 -1
  100. package/dist/web/assets/group-chat-tab-rOd8J8GC.js +1 -0
  101. package/dist/web/assets/{hack-DG151vit.js → hack-BzXbDMlA.js} +1 -1
  102. package/dist/web/assets/{haml-DKeIPQWM.js → haml-CPpkX0s5.js} +1 -1
  103. package/dist/web/assets/{handlebars-398uCI13.js → handlebars-BqrApy52.js} +1 -1
  104. package/dist/web/assets/{html-NLWdst5q.js → html-DmjUl-OA.js} +1 -1
  105. package/dist/web/assets/{html-derivative-CJf_Le2z.js → html-derivative-Cnd6XFR5.js} +1 -1
  106. package/dist/web/assets/{http-G1K2FozA.js → http-DMLWYun2.js} +1 -1
  107. package/dist/web/assets/{hurl-WDC6sSBz.js → hurl-CKkCVxbj.js} +1 -1
  108. package/dist/web/assets/{hxml-Dc3jBXuf.js → hxml-CAx19TtQ.js} +1 -1
  109. package/dist/web/assets/image-preview-DHUoylQ1.js +1 -0
  110. package/dist/web/assets/index-BImUaigO.js +52 -0
  111. package/dist/web/assets/index-CSfi6DSU.css +2 -0
  112. package/dist/web/assets/info-3K5VOQVL-BZi1Gha1.js +1 -0
  113. package/dist/web/assets/input-DcEk7mA3.js +1 -0
  114. package/dist/web/assets/{jinja-CWsQj2y0.js → jinja-BmB9YN9l.js} +1 -1
  115. package/dist/web/assets/{jison-CciJg2NW.js → jison-BaVsKKpw.js} +1 -1
  116. package/dist/web/assets/{julia-IOQNJAB7.js → julia-BgVBLveX.js} +1 -1
  117. package/dist/web/assets/{just-BGdHchwS.js → just-C2ENIbl9.js} +1 -1
  118. package/dist/web/assets/keybindings-store-BGIyqXHI.js +1 -0
  119. package/dist/web/assets/keybindings-store-CzV2sPls.js +1 -0
  120. package/dist/web/assets/{latex-D6TOl-L2.js → latex-BiDIWxKL.js} +1 -1
  121. package/dist/web/assets/{liquid-CVSvu_Wn.js → liquid-NxpapreE.js} +1 -1
  122. package/dist/web/assets/loader-circle-gSPVxfvH.js +1 -0
  123. package/dist/web/assets/lock-Did-87UV.js +1 -0
  124. package/dist/web/assets/{lua-CcKiU6HW.js → lua-DVepQvwH.js} +1 -1
  125. package/dist/web/assets/markdown-renderer-CcbR33VG.js +1 -0
  126. package/dist/web/assets/{markdown-renderer-B317vEys.js → markdown-renderer-DlcDhgrF.js} +3 -3
  127. package/dist/web/assets/{marko--UOicMGo.js → marko-CJ2606Xl.js} +1 -1
  128. package/dist/web/assets/{mdc-D690aU2C.js → mdc-D2bjqclE.js} +1 -1
  129. package/dist/web/assets/{media-url-CylYQld1.js → media-url-H17XJF-I.js} +1 -1
  130. package/dist/web/assets/mobile-bottom-sheet-g-r2hCFa.js +1 -0
  131. package/dist/web/assets/mobile-explorer-sheet-SIwQXlwA.js +1 -0
  132. package/dist/web/assets/monitor-Dv8vn_6O.js +1 -0
  133. package/dist/web/assets/{nextflow-DR8PhpF8.js → nextflow-BtJwUbfF.js} +1 -1
  134. package/dist/web/assets/{nginx-Qb9pUk1g.js → nginx-j4gyIhHE.js} +1 -1
  135. package/dist/web/assets/{nim-DLa8R3NS.js → nim-ClH_gjP5.js} +1 -1
  136. package/dist/web/assets/notification-store-DV5QtUTg.js +1 -0
  137. package/dist/web/assets/{number-overlay-editor-D9SkFg1Y.js → number-overlay-editor-C1EqJnWq.js} +1 -1
  138. package/dist/web/assets/{overview-panel-BxDohIEa.js → overview-panel-vTeeJYVZ.js} +1 -1
  139. package/dist/web/assets/packet-RMMSAZCW-o5htEcin.js +1 -0
  140. package/dist/web/assets/panel-store-BSnkjgHt.js +1 -0
  141. package/dist/web/assets/pdf-preview-CqFGRY0Z.js +1 -0
  142. package/dist/web/assets/pencil-BlIIg6RH.js +1 -0
  143. package/dist/web/assets/{perl-AMNGIneF.js → perl-C9H7X7CT.js} +1 -1
  144. package/dist/web/assets/{php-CDsOOxXn.js → php-Bx2U-cQ_.js} +1 -1
  145. package/dist/web/assets/pie-UPGHQEXC-BXAIFSAD.js +1 -0
  146. package/dist/web/assets/play-4MZi1Axp.js +1 -0
  147. package/dist/web/assets/portal-container-context-Cm5HipFr.js +1 -0
  148. package/dist/web/assets/{postgres-viewer-BWZLW7kI.js → postgres-viewer-DHwcQucn.js} +3 -3
  149. package/dist/web/assets/process-table-BGRJNlpn.js +1 -0
  150. package/dist/web/assets/{project-store-B8BkHXy5.js → project-store--nvQiy75.js} +1 -1
  151. package/dist/web/assets/{pug-C2kdkhQ6.js → pug-Cw74sKsE.js} +1 -1
  152. package/dist/web/assets/{qml-DRDkUp75.js → qml-BXD94Eqz.js} +1 -1
  153. package/dist/web/assets/radar-KQ55EAFF-Dbt2Aebl.js +1 -0
  154. package/dist/web/assets/{razor-wY_6wUeC.js → razor-DZ0-Ge0S.js} +1 -1
  155. package/dist/web/assets/refresh-cw-B06yaBrW.js +1 -0
  156. package/dist/web/assets/rotate-ccw-COXu_9oj.js +1 -0
  157. package/dist/web/assets/{rst-BRpofyNs.js → rst-Dtdh0QUj.js} +1 -1
  158. package/dist/web/assets/{ruby-ieATBhWm.js → ruby-BC51BW2Q.js} +1 -1
  159. package/dist/web/assets/{run-in-terminal-CyS5Itj-.js → run-in-terminal-Sd3KyjN4.js} +1 -1
  160. package/dist/web/assets/{sas-CoOOEbOO.js → sas-BN8i4qOU.js} +1 -1
  161. package/dist/web/assets/scroll-area-0gljZ6lF.js +1 -0
  162. package/dist/web/assets/{scss-_nWV0bsb.js → scss-H-QtA7Rz.js} +1 -1
  163. package/dist/web/assets/send-BlyNJAzy.js +1 -0
  164. package/dist/web/assets/{send-to-chat-BcO39BDR.js → send-to-chat-u6m8l-rH.js} +1 -1
  165. package/dist/web/assets/{settings-store-C20lqh0G.js → settings-store-CHY6Kzin.js} +2 -2
  166. package/dist/web/assets/settings-tab-Dic6TKkU.js +1 -0
  167. package/dist/web/assets/settings-tab-IY6K1MiZ.js +3 -0
  168. package/dist/web/assets/{shaderlab-XkqfIfO8.js → shaderlab-CalNsj_x.js} +1 -1
  169. package/dist/web/assets/shallow-DYo6goJ4.js +1 -0
  170. package/dist/web/assets/{shellsession-BKBIqTlM.js → shellsession-BFrcoR8x.js} +1 -1
  171. package/dist/web/assets/{soy-oVDnPzzh.js → soy-CNbku7vP.js} +1 -1
  172. package/dist/web/assets/{sparql-N2VclEsF.js → sparql-BN_orcVp.js} +1 -1
  173. package/dist/web/assets/{sql-query-editor-BwPjEaP9.js → sql-query-editor-C6pC3I8l.js} +1 -1
  174. package/dist/web/assets/sqlite-viewer-DTr5PzUb.js +1 -0
  175. package/dist/web/assets/{stata-BuuClVDg.js → stata-C6a5Ijzq.js} +1 -1
  176. package/dist/web/assets/{surrealql-DE5_63MS.js → surrealql-ClplI_HY.js} +1 -1
  177. package/dist/web/assets/{svelte--YWjlOrK.js → svelte-DNm-UNXB.js} +1 -1
  178. package/dist/web/assets/switch-DKvKya9y.js +1 -0
  179. package/dist/web/assets/system-monitor-body-iwGiCns9.js +2 -0
  180. package/dist/web/assets/system-monitor-tab-DdXQhEel.js +1 -0
  181. package/dist/web/assets/system-monitor-window-content-BUqRsPK2.js +1 -0
  182. package/dist/web/assets/tab-host-window-content-BnqGPu8a.js +1 -0
  183. package/dist/web/assets/{tab-store-DAsvpx4t.js → tab-store-CJuJRapl.js} +1 -1
  184. package/dist/web/assets/team-member-sheet-LkAFSCvb.js +2 -0
  185. package/dist/web/assets/team-member-window-content-T4Q5JI-G.js +1 -0
  186. package/dist/web/assets/{templ-MlpvJx4a.js → templ-CSdqVobu.js} +1 -1
  187. package/dist/web/assets/terminal-tab-Bb2rZegQ.js +3 -0
  188. package/dist/web/assets/{tex-0loAoXJG.js → tex-DzSJ4kcZ.js} +1 -1
  189. package/dist/web/assets/text-wrap-CBT4Gxtg.js +1 -0
  190. package/dist/web/assets/tool-cards-lhqhrDTT.js +8 -0
  191. package/dist/web/assets/trash-2-Bk4yYlck.js +1 -0
  192. package/dist/web/assets/treemap-KZPCXAKY-DUbnl9dz.js +1 -0
  193. package/dist/web/assets/triangle-alert-B8EYecpe.js +1 -0
  194. package/dist/web/assets/{ts-tags-pt-Ug0hK.js → ts-tags-C28w3bTg.js} +1 -1
  195. package/dist/web/assets/{twig-DOePpLOL.js → twig-i5jY3QXB.js} +1 -1
  196. package/dist/web/assets/upload-BNlX8D07.js +1 -0
  197. package/dist/web/assets/{use-blob-url-BKyVxOZw.js → use-blob-url-Dsq9OjTh.js} +1 -1
  198. package/dist/web/assets/{use-monaco-theme-DSCdlrjO.js → use-monaco-theme-BZvlsoBv.js} +1 -1
  199. package/dist/web/assets/use-open-team-member-DkiEjFYE.js +1 -0
  200. package/dist/web/assets/{use-websocket-iJfc7EbK.js → use-websocket-B8R50f7f.js} +1 -1
  201. package/dist/web/assets/{vendor-mermaid-DrwG0NYt.js → vendor-mermaid-By4AVvJY.js} +1 -1
  202. package/dist/web/assets/video-preview-m0-3p7QZ.js +1 -0
  203. package/dist/web/assets/{vue-D6f-rprj.js → vue-Ceyet_lv.js} +1 -1
  204. package/dist/web/assets/{vue-html-DEYRkS1V.js → vue-html-GuU7g_hg.js} +1 -1
  205. package/dist/web/assets/{vue-vine-CxSG2ojf.js → vue-vine-CxXoaJr2.js} +1 -1
  206. package/dist/web/assets/window-store-CsEW6jiE.js +1 -0
  207. package/dist/web/assets/{xml-DhrxbATj.js → xml-CbqOVhkP.js} +1 -1
  208. package/dist/web/assets/{xsl-CU0ZndUO.js → xsl-KfAmCauy.js} +1 -1
  209. package/dist/web/index.html +56 -27
  210. package/dist/web/sw.js +1 -1
  211. package/docs/code-standards.md +8 -0
  212. package/docs/codebase-summary.md +19 -5
  213. package/docs/design-guidelines.md +13 -2
  214. package/docs/project-roadmap.md +9 -0
  215. package/docs/system-architecture.md +83 -6
  216. package/package.json +1 -1
  217. package/src/web/components/chat/file-picker.tsx +5 -2
  218. package/src/web/components/chat/mode-selector.tsx +5 -2
  219. package/src/web/components/chat/model-selector.tsx +5 -2
  220. package/src/web/components/chat/model-thinking-selector.tsx +5 -2
  221. package/src/web/components/chat/provider-selector.tsx +5 -2
  222. package/src/web/components/chat/slash-command-picker.tsx +5 -2
  223. package/src/web/components/editor/code-editor.tsx +13 -3
  224. package/src/web/components/editor/conflict-editor.tsx +9 -0
  225. package/src/web/components/editor/diff-viewer.tsx +9 -0
  226. package/src/web/components/floating-window/floating-window.tsx +23 -15
  227. package/src/web/components/floating-window/pip/pip-caption-button.tsx +108 -0
  228. package/src/web/components/floating-window/pip/pip-focus-target.ts +58 -0
  229. package/src/web/components/floating-window/pip/pip-geometry.ts +46 -0
  230. package/src/web/components/floating-window/pip/pip-host.ts +156 -0
  231. package/src/web/components/floating-window/pip/pip-key-forward.ts +59 -0
  232. package/src/web/components/floating-window/pip/pip-resize-signal.ts +33 -0
  233. package/src/web/components/floating-window/pip/pip-style-copy.ts +108 -0
  234. package/src/web/components/floating-window/pip/pip-support.ts +33 -0
  235. package/src/web/components/floating-window/tab-host-window-content.tsx +80 -0
  236. package/src/web/components/floating-window/use-window-body-element.ts +67 -0
  237. package/src/web/components/floating-window/use-window-drag.ts +5 -0
  238. package/src/web/components/floating-window/use-window-gesture-context.ts +14 -0
  239. package/src/web/components/floating-window/use-window-resize.ts +5 -0
  240. package/src/web/components/floating-window/window-content-registry.ts +4 -16
  241. package/src/web/components/floating-window/window-layer.tsx +1 -2
  242. package/src/web/components/floating-window/window-persistence.ts +9 -2
  243. package/src/web/components/floating-window/window-pip-placeholder.tsx +31 -0
  244. package/src/web/components/floating-window/window-pip-registry.ts +69 -0
  245. package/src/web/components/floating-window/window-skin-chrome.tsx +21 -0
  246. package/src/web/components/floating-window/window-store-types.ts +8 -2
  247. package/src/web/components/layout/reparenting-tab.tsx +136 -0
  248. package/src/web/components/layout/tab-bar.tsx +2 -0
  249. package/src/web/components/layout/tab-pool-collect.ts +38 -2
  250. package/src/web/components/layout/tab-pool-registry.ts +48 -0
  251. package/src/web/components/layout/tab-pool.tsx +54 -168
  252. package/src/web/components/layout/tab-pop-out-menu-item.tsx +42 -0
  253. package/src/web/components/layout/use-window-panel-reconcile.ts +48 -0
  254. package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +16 -7
  255. package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +14 -8
  256. package/src/web/components/ui/context-menu.tsx +10 -3
  257. package/src/web/components/ui/dialog.tsx +10 -1
  258. package/src/web/components/ui/dropdown-menu.tsx +10 -3
  259. package/src/web/components/ui/mobile-bottom-sheet.tsx +7 -4
  260. package/src/web/components/ui/portal-container-context.tsx +40 -0
  261. package/src/web/components/ui/select.tsx +3 -1
  262. package/src/web/components/ui/tooltip.tsx +3 -1
  263. package/src/web/hooks/use-terminal.ts +24 -3
  264. package/src/web/stores/panel-store.ts +72 -7
  265. package/src/web/stores/panel-utils.ts +36 -0
  266. package/src/web/stores/window-panel-actions.ts +211 -0
  267. package/src/web/stores/window-panel-persistence.ts +115 -0
  268. package/src/web/stores/window-panel-reconcile.ts +97 -0
  269. package/dist/web/assets/ai-resource-editor-mdDKG-rw.js +0 -1
  270. package/dist/web/assets/api-client-CzABTreM.js +0 -1
  271. package/dist/web/assets/api-settings-nblja3Rk.js +0 -1
  272. package/dist/web/assets/architecture-PBZL5I3N-C7ra20CE.js +0 -1
  273. package/dist/web/assets/chat-tab-C060A94y.js +0 -15
  274. package/dist/web/assets/code-editor-WELegzdp.js +0 -10
  275. package/dist/web/assets/context-menu-Cq0u3SK9.js +0 -1
  276. package/dist/web/assets/database-viewer-DevnWTV4.js +0 -1
  277. package/dist/web/assets/diff-viewer-DGxVg1pD.js +0 -4
  278. package/dist/web/assets/docx-preview-Cx-zLwAw.js +0 -12
  279. package/dist/web/assets/explorer-body-j_4hVc34.js +0 -2
  280. package/dist/web/assets/explorer-window-content-DdrGwk3w.js +0 -1
  281. package/dist/web/assets/git-log-panel-CfpXRJcP.js +0 -1
  282. package/dist/web/assets/gitGraph-HDMCJU4V-D4hYLNJU.js +0 -1
  283. package/dist/web/assets/group-chat-tab-dYuuknLN.js +0 -1
  284. package/dist/web/assets/image-preview-Ss38ex8g.js +0 -1
  285. package/dist/web/assets/index-BiTiXgBZ.css +0 -2
  286. package/dist/web/assets/index-Ch8l9de-.js +0 -52
  287. package/dist/web/assets/info-3K5VOQVL-lRlrTlkb.js +0 -1
  288. package/dist/web/assets/keybindings-store-CDfTjwZX.js +0 -1
  289. package/dist/web/assets/markdown-renderer-CpRlpLHw.js +0 -1
  290. package/dist/web/assets/mobile-bottom-sheet-BUOJgiaZ.js +0 -1
  291. package/dist/web/assets/mobile-explorer-sheet-D1Bv9UCT.js +0 -1
  292. package/dist/web/assets/notification-store-KwTAZT8b.js +0 -1
  293. package/dist/web/assets/packet-RMMSAZCW-BIhk1mvp.js +0 -1
  294. package/dist/web/assets/panel-store-DCD82zc1.js +0 -1
  295. package/dist/web/assets/pdf-preview-dwRe54ht.js +0 -1
  296. package/dist/web/assets/pie-UPGHQEXC-SLS6i76l.js +0 -1
  297. package/dist/web/assets/process-table-CI-fnVsy.js +0 -1
  298. package/dist/web/assets/radar-KQ55EAFF-JJ6O1Er_.js +0 -1
  299. package/dist/web/assets/settings-tab-mPxrKkfm.js +0 -1
  300. package/dist/web/assets/sqlite-viewer-Cs-_D942.js +0 -1
  301. package/dist/web/assets/system-monitor-body-CV7-0hZG.js +0 -2
  302. package/dist/web/assets/system-monitor-tab-Ce-8Axhh.js +0 -1
  303. package/dist/web/assets/system-monitor-window-content-CetwYOQy.js +0 -1
  304. package/dist/web/assets/team-member-sheet-CjgaQjsZ.js +0 -2
  305. package/dist/web/assets/team-member-window-content-DNJiYWS7.js +0 -1
  306. package/dist/web/assets/terminal-tab-DF7521sT.js +0 -3
  307. package/dist/web/assets/tool-cards-DCAXGDJI.js +0 -8
  308. package/dist/web/assets/treemap-KZPCXAKY-Be3KNmns.js +0 -1
  309. package/dist/web/assets/use-open-team-member-H4k74HnV.js +0 -1
  310. package/dist/web/assets/video-preview-B0ZWpX0F.js +0 -1
  311. package/src/web/components/floating-window/default-window-chrome.tsx +0 -71
  312. package/src/web/components/os-explorer/skins/explorer-skin-chrome.tsx +0 -17
  313. /package/dist/web/assets/{abap-MRbmsXOs.js → abap-C6nB04tn.js} +0 -0
  314. /package/dist/web/assets/{actionscript-3-BDDHKqa4.js → actionscript-3-Vx4rw2sC.js} +0 -0
  315. /package/dist/web/assets/{ada-DpuV9KwV.js → ada-Ccd8vM20.js} +0 -0
  316. /package/dist/web/assets/{andromeeda-D6qUCKJo.js → andromeeda-DnBX5xMg.js} +0 -0
  317. /package/dist/web/assets/{apache-CX1NU_Tl.js → apache-B-9_sx0g.js} +0 -0
  318. /package/dist/web/assets/{apex-B4-aoQ3K.js → apex-_wPVBYoL.js} +0 -0
  319. /package/dist/web/assets/{api-client-BSB14J4w.js → api-client-aQb5hHE5.js} +0 -0
  320. /package/dist/web/assets/{applescript-Dm9NI6KR.js → applescript-CcvD-qr0.js} +0 -0
  321. /package/dist/web/assets/{apply-theme-BH_d4amk.js → apply-theme-BeLRfsSu.js} +0 -0
  322. /package/dist/web/assets/{ara-BGN8GAYn.js → ara-C40Ztoms.js} +0 -0
  323. /package/dist/web/assets/{asciidoc-s_yVuR3t.js → asciidoc-CybYkJV4.js} +0 -0
  324. /package/dist/web/assets/{asm-BzQdSvTe.js → asm-9eOKwzR0.js} +0 -0
  325. /package/dist/web/assets/{aurora-x-BJTLETG_.js → aurora-x-B5ZH4t7t.js} +0 -0
  326. /package/dist/web/assets/{awk-BQEGWxvX.js → awk-Dqilob-2.js} +0 -0
  327. /package/dist/web/assets/{ayu-dark-CpTORmLc.js → ayu-dark-CPgRhf5r.js} +0 -0
  328. /package/dist/web/assets/{ayu-light-D8PVj-e6.js → ayu-light-Do06Q52f.js} +0 -0
  329. /package/dist/web/assets/{ayu-mirage-DyElQHtm.js → ayu-mirage-_BhxcrxY.js} +0 -0
  330. /package/dist/web/assets/{ballerina-C82NkRtj.js → ballerina-DDwK54QW.js} +0 -0
  331. /package/dist/web/assets/{bat-0X7DtGI-.js → bat-BGyGlzGk.js} +0 -0
  332. /package/dist/web/assets/{beancount-Xgvy6YRl.js → beancount-CRWBdapI.js} +0 -0
  333. /package/dist/web/assets/{berry-CCT2pZcQ.js → berry-DbSFn2gO.js} +0 -0
  334. /package/dist/web/assets/{bibtex-CpXgZ9M_.js → bibtex-QpsV9gm9.js} +0 -0
  335. /package/dist/web/assets/{bicep-fkeSA62l.js → bicep-CxS8CUve.js} +0 -0
  336. /package/dist/web/assets/{bird2-Co7SeAI8.js → bird2-CuZhQzYR.js} +0 -0
  337. /package/dist/web/assets/{bot-CunPuEdW.js → bot-D6sx11y0.js} +0 -0
  338. /package/dist/web/assets/{c-BpmrTvJh.js → c-9bnDqE3g.js} +0 -0
  339. /package/dist/web/assets/{c3-BZ_Z0RrR.js → c3-JMZTrOhA.js} +0 -0
  340. /package/dist/web/assets/{cadence-B0GBC8OF.js → cadence-DsDNayJS.js} +0 -0
  341. /package/dist/web/assets/{can-open-in-ppm-omBzUBBq.js → can-open-in-ppm-BJfpynya.js} +0 -0
  342. /package/dist/web/assets/{catppuccin-frappe-DIt_xR7m.js → catppuccin-frappe-DYIKXXBe.js} +0 -0
  343. /package/dist/web/assets/{catppuccin-latte-Dw40AcqA.js → catppuccin-latte-IYYOnUJ8.js} +0 -0
  344. /package/dist/web/assets/{catppuccin-macchiato-BJ7DfVEF.js → catppuccin-macchiato-BrJ_7mfS.js} +0 -0
  345. /package/dist/web/assets/{catppuccin-mocha-NI2lfXzU.js → catppuccin-mocha-gzSu7v7-.js} +0 -0
  346. /package/dist/web/assets/{chevron-right-BLOtX8Vy.js → chevron-right-De-Wz5Lz.js} +0 -0
  347. /package/dist/web/assets/{circle-hjUGru6R.js → circle-BCy0AUCo.js} +0 -0
  348. /package/dist/web/assets/{clarity-F39L0xMJ.js → clarity-DLzsC0Hb.js} +0 -0
  349. /package/dist/web/assets/{clock-CvhF4DJP.js → clock-C1CQUQPZ.js} +0 -0
  350. /package/dist/web/assets/{clojure-DloMkd8K.js → clojure-DS1BvoTt.js} +0 -0
  351. /package/dist/web/assets/{cmake-CwerJun_.js → cmake-CDvsJwYp.js} +0 -0
  352. /package/dist/web/assets/{code-DDPoqVri.js → code-DJgYrFzm.js} +0 -0
  353. /package/dist/web/assets/{codeowners-CQyVz4Qf.js → codeowners-BjsYiflJ.js} +0 -0
  354. /package/dist/web/assets/{codeql-Bb229zuY.js → codeql-CG-7ARat.js} +0 -0
  355. /package/dist/web/assets/{common-lisp-KMlIwq5I.js → common-lisp-DSO1Lbh9.js} +0 -0
  356. /package/dist/web/assets/{copy-DKn11Pye.js → copy-BewHj66E.js} +0 -0
  357. /package/dist/web/assets/{coq-DHX_3IIo.js → coq-DYdh3oP_.js} +0 -0
  358. /package/dist/web/assets/{csharp-CEmCZv9Q.js → csharp-DxEBz5s0.js} +0 -0
  359. /package/dist/web/assets/{css-CqZqBD05.js → css-dal3Q-8C.js} +0 -0
  360. /package/dist/web/assets/{csv-l1g_V5MV.js → csv-CpqjMGiA.js} +0 -0
  361. /package/dist/web/assets/{csv-parser-CnS0wqr6.js → csv-parser-3umBA4NY.js} +0 -0
  362. /package/dist/web/assets/{cue-BrxRmilg.js → cue-DinoJ_5k.js} +0 -0
  363. /package/dist/web/assets/{cypher-rZkKwqZc.js → cypher-DzxqmkUW.js} +0 -0
  364. /package/dist/web/assets/{d-BALR-QuP.js → d-BxNwJuEC.js} +0 -0
  365. /package/dist/web/assets/{dark-plus-Cj7rVwh-.js → dark-plus-Clmv3V-0.js} +0 -0
  366. /package/dist/web/assets/{dart-CWQcdkXk.js → dart-BWVtXD35.js} +0 -0
  367. /package/dist/web/assets/{data-grid-types-CYvF-xK5.js → data-grid-types-B4n51Fck.js} +0 -0
  368. /package/dist/web/assets/{database-BH0urvbs.js → database-Y3ESDK4-.js} +0 -0
  369. /package/dist/web/assets/{dax-BNHD-QBA.js → dax-DsImuWgJ.js} +0 -0
  370. /package/dist/web/assets/{desktop-CN0IXPrP.js → desktop-B0jJuG2r.js} +0 -0
  371. /package/dist/web/assets/{diff-BtOXapES.js → diff-CWqAAxoE.js} +0 -0
  372. /package/dist/web/assets/{dist-DB1Oaqcc.js → dist-B-EXdSYE.js} +0 -0
  373. /package/dist/web/assets/{dist-Di6G_5jU.js → dist-C0eNNfbt.js} +0 -0
  374. /package/dist/web/assets/{docker-DNsAo_qO.js → docker-CYFCJdIL.js} +0 -0
  375. /package/dist/web/assets/{dotenv-D6Pl6aOU.js → dotenv-CKDUjURg.js} +0 -0
  376. /package/dist/web/assets/{download-zCATy4x4.js → download-DG3_9xXy.js} +0 -0
  377. /package/dist/web/assets/{dracula-C3m-w12D.js → dracula-CSoPspHE.js} +0 -0
  378. /package/dist/web/assets/{dracula-soft-BYqvXm6Z.js → dracula-soft-BuFjw5L1.js} +0 -0
  379. /package/dist/web/assets/{dream-maker-DZAPbPTi.js → dream-maker-B6PdDRZu.js} +0 -0
  380. /package/dist/web/assets/{emacs-lisp-Ao5SW8ay.js → emacs-lisp-CMcXvD0G.js} +0 -0
  381. /package/dist/web/assets/{everforest-dark-uC8QYotQ.js → everforest-dark-cU8-wYzi.js} +0 -0
  382. /package/dist/web/assets/{everforest-light-CTgsVNMr.js → everforest-light-KhorllqK.js} +0 -0
  383. /package/dist/web/assets/{fennel-WuBw6IR1.js → fennel-BCELZK87.js} +0 -0
  384. /package/dist/web/assets/{file-exclamation-point-COAwgNGw.js → file-exclamation-point-CZ4DiR75.js} +0 -0
  385. /package/dist/web/assets/{fish-Byy9dusu.js → fish-BQc0y_jy.js} +0 -0
  386. /package/dist/web/assets/{fluent-CDxb_sgw.js → fluent-BTINnwF6.js} +0 -0
  387. /package/dist/web/assets/{format-bytes-BE_W3UBg.js → format-bytes-WVcFxJuT.js} +0 -0
  388. /package/dist/web/assets/{fortran-free-form-DKWSTamT.js → fortran-free-form-BQim9wDz.js} +0 -0
  389. /package/dist/web/assets/{gdscript-prYHMgtD.js → gdscript-03w5u8un.js} +0 -0
  390. /package/dist/web/assets/{gdshader-D-RM-SQk.js → gdshader-Dp2daNzU.js} +0 -0
  391. /package/dist/web/assets/{genie-CowkJhej.js → genie-CoNcjPiD.js} +0 -0
  392. /package/dist/web/assets/{gherkin-CLFpoVYJ.js → gherkin-NrPOSPQL.js} +0 -0
  393. /package/dist/web/assets/{github-dark-default-BK_1Qgb8.js → github-dark-default-DOKw-1cI.js} +0 -0
  394. /package/dist/web/assets/{github-dark-dimmed-Cv9lj6f-.js → github-dark-dimmed-DJRNMkEA.js} +0 -0
  395. /package/dist/web/assets/{github-dark-high-contrast-CW5ZgIDh.js → github-dark-high-contrast-Bj7jJ_Gs.js} +0 -0
  396. /package/dist/web/assets/{github-dark-nW2F-RLd.js → github-dark-z4RE9ZqA.js} +0 -0
  397. /package/dist/web/assets/{github-light-D4PqBHGZ.js → github-light-B4k_yMFu.js} +0 -0
  398. /package/dist/web/assets/{github-light-default-B-cHtJJV.js → github-light-default-BHc-Uzhs.js} +0 -0
  399. /package/dist/web/assets/{github-light-high-contrast-Dt0HJq50.js → github-light-high-contrast-CHisIywL.js} +0 -0
  400. /package/dist/web/assets/{gleam-DwSyXJWP.js → gleam-CfbypZ4q.js} +0 -0
  401. /package/dist/web/assets/{gn-Cv5WEzHc.js → gn-YoAjyhJa.js} +0 -0
  402. /package/dist/web/assets/{gnuplot-D7bRdY_M.js → gnuplot-xVkc1f1c.js} +0 -0
  403. /package/dist/web/assets/{go-DiRVJ22U.js → go-GPCwP3Gu.js} +0 -0
  404. /package/dist/web/assets/{groovy-BHAgLTeO.js → groovy-Crh4Yww9.js} +0 -0
  405. /package/dist/web/assets/{gruvbox-dark-hard-C8QZ3gzY.js → gruvbox-dark-hard-BluUFuDS.js} +0 -0
  406. /package/dist/web/assets/{gruvbox-dark-medium-Dp1eeid2.js → gruvbox-dark-medium-Cxk1FWQp.js} +0 -0
  407. /package/dist/web/assets/{gruvbox-dark-soft-Cwq0dWVj.js → gruvbox-dark-soft-CHXMiFmj.js} +0 -0
  408. /package/dist/web/assets/{gruvbox-light-hard-CLQCq2-w.js → gruvbox-light-hard-B6eKPjAD.js} +0 -0
  409. /package/dist/web/assets/{gruvbox-light-medium-APHsh6Mo.js → gruvbox-light-medium-pDwMm1ut.js} +0 -0
  410. /package/dist/web/assets/{gruvbox-light-soft-CX-fYc64.js → gruvbox-light-soft-Cxu6kHqK.js} +0 -0
  411. /package/dist/web/assets/{haskell-C4Tw-TDa.js → haskell-C6JKHjbK.js} +0 -0
  412. /package/dist/web/assets/{haxe-B7fSduTQ.js → haxe-BuBNdF0a.js} +0 -0
  413. /package/dist/web/assets/{hcl-Bi3ho6yC.js → hcl-DZpDdGj2.js} +0 -0
  414. /package/dist/web/assets/{hjson-BXwae2M1.js → hjson-CBkhYRuo.js} +0 -0
  415. /package/dist/web/assets/{hlsl-CRuQLBSa.js → hlsl-C5nMxnp9.js} +0 -0
  416. /package/dist/web/assets/{horizon-DtdQ6qjv.js → horizon-VAFvA6R6.js} +0 -0
  417. /package/dist/web/assets/{horizon-bright-BXSF_uKq.js → horizon-bright-BC0xn7T3.js} +0 -0
  418. /package/dist/web/assets/{houston-C6ASpUjT.js → houston-BxrUivmz.js} +0 -0
  419. /package/dist/web/assets/{hy-BrWBHtHY.js → hy-DLtOXoaH.js} +0 -0
  420. /package/dist/web/assets/{image-gallery-CGvlv8zR.js → image-gallery-Gnf7TdCR.js} +0 -0
  421. /package/dist/web/assets/{imba-C_JJXckG.js → imba-BdpFXM2W.js} +0 -0
  422. /package/dist/web/assets/{ini-DEE_vSHQ.js → ini-BInJO_T0.js} +0 -0
  423. /package/dist/web/assets/{java-CnmpcjcT.js → java-Dxuddnkb.js} +0 -0
  424. /package/dist/web/assets/{javascript-C-M7sa2e.js → javascript-CVamJfLY.js} +0 -0
  425. /package/dist/web/assets/{json-BjZ3dAk6.js → json-BLZ0ozYk.js} +0 -0
  426. /package/dist/web/assets/{json5-BQ2vpqjp.js → json5-BnXzmMKs.js} +0 -0
  427. /package/dist/web/assets/{jsonc-C5MjoWGx.js → jsonc-CSgkSga7.js} +0 -0
  428. /package/dist/web/assets/{jsonl-JvhrmQJI.js → jsonl-CY5W2nF2.js} +0 -0
  429. /package/dist/web/assets/{jsonnet-C5pdZVuo.js → jsonnet-CEJYH3sF.js} +0 -0
  430. /package/dist/web/assets/{jssm-Db17uybX.js → jssm-CsJc7Uhk.js} +0 -0
  431. /package/dist/web/assets/{jsx-tJp2Cyqu.js → jsx-jCiRaHU7.js} +0 -0
  432. /package/dist/web/assets/{kanagawa-dragon-BculT_Y1.js → kanagawa-dragon-DfoA_GxX.js} +0 -0
  433. /package/dist/web/assets/{kanagawa-lotus-yQgHUGwU.js → kanagawa-lotus-BOLYJpWd.js} +0 -0
  434. /package/dist/web/assets/{kanagawa-wave-CyGoWN4d.js → kanagawa-wave-DfKlbT1G.js} +0 -0
  435. /package/dist/web/assets/{kdl-Bn8R1REF.js → kdl-CmssIFTi.js} +0 -0
  436. /package/dist/web/assets/{kotlin-DWughK73.js → kotlin-BRsNgp6x.js} +0 -0
  437. /package/dist/web/assets/{kusto--serPb0x.js → kusto-T8X9jUab.js} +0 -0
  438. /package/dist/web/assets/{laserwave-DfAPJ1tt.js → laserwave-DkJMVJ8Z.js} +0 -0
  439. /package/dist/web/assets/{lean-UAm1HOKl.js → lean-C65KGVPq.js} +0 -0
  440. /package/dist/web/assets/{less-Xj6Ka0fF.js → less-exIPdDlf.js} +0 -0
  441. /package/dist/web/assets/{lib-DgNcCtj3.js → lib-mC1NZfuw.js} +0 -0
  442. /package/dist/web/assets/{libesm-CPh1Av0r.js → libesm-DSutUtl1.js} +0 -0
  443. /package/dist/web/assets/{light-plus-BfS7gPcj.js → light-plus-DiHUXAZt.js} +0 -0
  444. /package/dist/web/assets/{llvm-DvWiVwvU.js → llvm-CJBnEzFM.js} +0 -0
  445. /package/dist/web/assets/{log-Bo5z9bNz.js → log-DxMyUMuP.js} +0 -0
  446. /package/dist/web/assets/{logo-CrIjcMZT.js → logo-CC9NKzca.js} +0 -0
  447. /package/dist/web/assets/{luau-C9TsYwNt.js → luau-Bw10I6zf.js} +0 -0
  448. /package/dist/web/assets/{make-GYq4qVbx.js → make-C_6M90m6.js} +0 -0
  449. /package/dist/web/assets/{markdown-p5mNledS.js → markdown-bR9_R-LF.js} +0 -0
  450. /package/dist/web/assets/{material-theme-CWYOVxsP.js → material-theme-BUx2Qujn.js} +0 -0
  451. /package/dist/web/assets/{material-theme-darker-BDGty7YH.js → material-theme-darker-CRJrPgKm.js} +0 -0
  452. /package/dist/web/assets/{material-theme-lighter-JbgOgV3n.js → material-theme-lighter-CUAYppPa.js} +0 -0
  453. /package/dist/web/assets/{material-theme-ocean-EMOpLpa2.js → material-theme-ocean-DuINF1fg.js} +0 -0
  454. /package/dist/web/assets/{material-theme-palenight-CY5hgm7h.js → material-theme-palenight-W6_icGCj.js} +0 -0
  455. /package/dist/web/assets/{matlab-C-He_NTH.js → matlab-rfqn_b3z.js} +0 -0
  456. /package/dist/web/assets/{mdx-Cr2TR5fj.js → mdx-Bh1B_YOJ.js} +0 -0
  457. /package/dist/web/assets/{mermaid-BYc0-B0U.js → mermaid-Dxf6Ya2k.js} +0 -0
  458. /package/dist/web/assets/{min-dark-CMJwqJqB.js → min-dark-CivZ2L0h.js} +0 -0
  459. /package/dist/web/assets/{min-light-Cx6vFiEX.js → min-light-B1Ykqtma.js} +0 -0
  460. /package/dist/web/assets/{mipsasm-Bjer5HwK.js → mipsasm-DynHaM0d.js} +0 -0
  461. /package/dist/web/assets/{mojo-t90PR-Y2.js → mojo-Clw5PbGH.js} +0 -0
  462. /package/dist/web/assets/{monokai-dci_PdCm.js → monokai-DFIooFZT.js} +0 -0
  463. /package/dist/web/assets/{moonbit-AAzI-Hd-.js → moonbit-DnBSZWGM.js} +0 -0
  464. /package/dist/web/assets/{move-kwBhnhrG.js → move-CCcL2PfL.js} +0 -0
  465. /package/dist/web/assets/{narrat-CXww2YQL.js → narrat-BDarg5Lk.js} +0 -0
  466. /package/dist/web/assets/{nextflow-groovy-CJXsuXGB.js → nextflow-groovy-DrZOeCNW.js} +0 -0
  467. /package/dist/web/assets/{night-owl-_GD204Wr.js → night-owl-BnZZCK7v.js} +0 -0
  468. /package/dist/web/assets/{night-owl-light-DNgkw3kl.js → night-owl-light-CEwBsVHS.js} +0 -0
  469. /package/dist/web/assets/{nix-BgiorKFd.js → nix-amCzOPMR.js} +0 -0
  470. /package/dist/web/assets/{nord-CaBmcxFC.js → nord-BIs2BC6q.js} +0 -0
  471. /package/dist/web/assets/{nushell-DUh4UGx1.js → nushell-Y02fo4yw.js} +0 -0
  472. /package/dist/web/assets/{objective-c-Cl9KU86S.js → objective-c-7xOZtWW1.js} +0 -0
  473. /package/dist/web/assets/{objective-cpp-DVkIA8A0.js → objective-cpp-eVtjUvVB.js} +0 -0
  474. /package/dist/web/assets/{ocaml-vvoHTak0.js → ocaml-Bi6ZwqG4.js} +0 -0
  475. /package/dist/web/assets/{odin-Cyjw8woZ.js → odin-8xf1Y0S9.js} +0 -0
  476. /package/dist/web/assets/{one-dark-pro-B1cviILi.js → one-dark-pro-hVv-_y_b.js} +0 -0
  477. /package/dist/web/assets/{one-light-BPGUJeYI.js → one-light-CJJ3MtHt.js} +0 -0
  478. /package/dist/web/assets/{openscad-BLMdTUU8.js → openscad-B2HcywLk.js} +0 -0
  479. /package/dist/web/assets/{pascal-i5niy3e7.js → pascal-C7fllV_7.js} +0 -0
  480. /package/dist/web/assets/{pkl-CuOV7SsU.js → pkl-qvLC1IWP.js} +0 -0
  481. /package/dist/web/assets/{plastic-Bf_zE5KL.js → plastic-BT1wHZv2.js} +0 -0
  482. /package/dist/web/assets/{plsql-cfqEtMD3.js → plsql-1MsmdmJD.js} +0 -0
  483. /package/dist/web/assets/{plus-BM-Ty0fp.js → plus-BhMZqFsN.js} +0 -0
  484. /package/dist/web/assets/{po-CFJcWgjr.js → po-BigskytD.js} +0 -0
  485. /package/dist/web/assets/{poimandres-G5YoaIFf.js → poimandres-DuklniF7.js} +0 -0
  486. /package/dist/web/assets/{polar-rcjs3i9R.js → polar-e2pjN_sp.js} +0 -0
  487. /package/dist/web/assets/{postcss-CBKY1FTZ.js → postcss-DtAeYjWw.js} +0 -0
  488. /package/dist/web/assets/{powerquery-Db7SXLbO.js → powerquery-Cr2PtehI.js} +0 -0
  489. /package/dist/web/assets/{powershell-LFy3MUTP.js → powershell-BxFTGLJ4.js} +0 -0
  490. /package/dist/web/assets/{prisma-BE8GR1lH.js → prisma-COWJiwdZ.js} +0 -0
  491. /package/dist/web/assets/{prolog-DOn5lR1e.js → prolog-DFvGKRx6.js} +0 -0
  492. /package/dist/web/assets/{proto-wr-TpAK_.js → proto-CcK80lFt.js} +0 -0
  493. /package/dist/web/assets/{puppet-D2Dkv62u.js → puppet-BF60bZ4F.js} +0 -0
  494. /package/dist/web/assets/{purescript-DvgJgPdy.js → purescript-zL_69MzU.js} +0 -0
  495. /package/dist/web/assets/{python-C7TlkHhK.js → python-CIwKhfb_.js} +0 -0
  496. /package/dist/web/assets/{qmldir-CtZMwhcW.js → qmldir-CXNe04Lo.js} +0 -0
  497. /package/dist/web/assets/{qss-dNZ2btnl.js → qss-C8lAWe6G.js} +0 -0
  498. /package/dist/web/assets/{r-BlqnNkHS.js → r-CL5ZVL3_.js} +0 -0
  499. /package/dist/web/assets/{racket-CzRQrGc4.js → racket-BBfh0uh0.js} +0 -0
  500. /package/dist/web/assets/{raku-BwutWhBF.js → raku-o6E8R5Q3.js} +0 -0
  501. /package/dist/web/assets/{react-I_EwPIZi.js → react-CBTnT4sY.js} +0 -0
  502. /package/dist/web/assets/{red-BMXMywmL.js → red-GomRF2Xp.js} +0 -0
  503. /package/dist/web/assets/{reg-BqOKrIOw.js → reg-_ffDIvx9.js} +0 -0
  504. /package/dist/web/assets/{regexp-Bero1BN2.js → regexp-Bl5sF65W.js} +0 -0
  505. /package/dist/web/assets/{rel-BXKWIElA.js → rel-DgMdqDuj.js} +0 -0
  506. /package/dist/web/assets/{resolve-theme-B9ABAH_3.js → resolve-theme-CRt_7wpd.js} +0 -0
  507. /package/dist/web/assets/{riscv-BbbBYJBo.js → riscv-8b3-Ye4X.js} +0 -0
  508. /package/dist/web/assets/{ron-BtVTWW4b.js → ron-DrmU1KHP.js} +0 -0
  509. /package/dist/web/assets/{rose-pine-hvkvQzvU.js → rose-pine-C_-E2tF1.js} +0 -0
  510. /package/dist/web/assets/{rose-pine-dawn-D7lwhUei.js → rose-pine-dawn-DEfhNlFH.js} +0 -0
  511. /package/dist/web/assets/{rose-pine-moon-CoHDmsIw.js → rose-pine-moon-BdvX3gdn.js} +0 -0
  512. /package/dist/web/assets/{rosmsg-CdaZFgI8.js → rosmsg-B6GhGpd5.js} +0 -0
  513. /package/dist/web/assets/{rust-Dy2qsphD.js → rust-DmC97M1-.js} +0 -0
  514. /package/dist/web/assets/{sass-Bz7NVxDb.js → sass-B0tJOHLM.js} +0 -0
  515. /package/dist/web/assets/{save-C4z1p51e.js → save-CtIGOjmx.js} +0 -0
  516. /package/dist/web/assets/{scala-sXSwsNyq.js → scala-n8zaFOXv.js} +0 -0
  517. /package/dist/web/assets/{scheme-DS7TUbN7.js → scheme-BKD9ztBU.js} +0 -0
  518. /package/dist/web/assets/{sdbl-iISFvIep.js → sdbl-D8gB5-rS.js} +0 -0
  519. /package/dist/web/assets/{shellscript-BkIl4ApC.js → shellscript-BfXlf_Ao.js} +0 -0
  520. /package/dist/web/assets/{shield-check-C5laObJv.js → shield-check-B0iGUiOK.js} +0 -0
  521. /package/dist/web/assets/{shield-off-DULFtyLS.js → shield-off-BNLWBhcq.js} +0 -0
  522. /package/dist/web/assets/{slack-dark-B1eg0_m2.js → slack-dark-CylEddse.js} +0 -0
  523. /package/dist/web/assets/{slack-ochin-mdkc1ldu.js → slack-ochin-w8wg8vuC.js} +0 -0
  524. /package/dist/web/assets/{smalltalk-CORtd03t.js → smalltalk-hiAXA64u.js} +0 -0
  525. /package/dist/web/assets/{snazzy-light-Bc6Q2G89.js → snazzy-light-BwlV3Xty.js} +0 -0
  526. /package/dist/web/assets/{solarized-dark-CF83zRXM.js → solarized-dark-vmNGdykz.js} +0 -0
  527. /package/dist/web/assets/{solarized-light-BoKQZaYU.js → solarized-light-DGv_0JXq.js} +0 -0
  528. /package/dist/web/assets/{solidity-DOgi1N4w.js → solidity-Y7QSH0B1.js} +0 -0
  529. /package/dist/web/assets/{sparkles-Dqsf3hq6.js → sparkles-Dh-_J2Rd.js} +0 -0
  530. /package/dist/web/assets/{splunk-BXfw5ZLy.js → splunk-BtgpLjHi.js} +0 -0
  531. /package/dist/web/assets/{sql-BcVs-eX4.js → sql-CRHYhOia.js} +0 -0
  532. /package/dist/web/assets/{ssh-config-CLCtNkkd.js → ssh-config-DOqSs3au.js} +0 -0
  533. /package/dist/web/assets/{stylus-DdP992aO.js → stylus-crCAEaBT.js} +0 -0
  534. /package/dist/web/assets/{swift-BnGb6Lgc.js → swift-eE3rT7TC.js} +0 -0
  535. /package/dist/web/assets/{synthwave-84-cg9264l9.js → synthwave-84-BWy14LBX.js} +0 -0
  536. /package/dist/web/assets/{system-verilog-GbHpszs3.js → system-verilog-DC0-DZM4.js} +0 -0
  537. /package/dist/web/assets/{systemd-Dz-bNsDQ.js → systemd-to5p6N8I.js} +0 -0
  538. /package/dist/web/assets/{table-CHmxuh6M.js → table-Bbc783ko.js} +0 -0
  539. /package/dist/web/assets/{talonscript-CFIidoBA.js → talonscript-DoTcU8wm.js} +0 -0
  540. /package/dist/web/assets/{tasl-Cu-TRTKx.js → tasl-cNZvUs95.js} +0 -0
  541. /package/dist/web/assets/{tcl-DwCHCB1K.js → tcl-mZE8sZH8.js} +0 -0
  542. /package/dist/web/assets/{terraform-BzZw4T1z.js → terraform-CqHPpIfu.js} +0 -0
  543. /package/dist/web/assets/{tokyo-night-RKsj2-tv.js → tokyo-night-Tp2rTIZd.js} +0 -0
  544. /package/dist/web/assets/{toml-CirbOgDP.js → toml-CU_vscgn.js} +0 -0
  545. /package/dist/web/assets/{tsv-CraRcWdU.js → tsv-ik7iFB9D.js} +0 -0
  546. /package/dist/web/assets/{tsx-CjFg0lgw.js → tsx-BMvvMKiv.js} +0 -0
  547. /package/dist/web/assets/{turtle-B-WBIOa6.js → turtle-DBqKCq9o.js} +0 -0
  548. /package/dist/web/assets/{typescript-MpLarYn1.js → typescript-DXJDmMZZ.js} +0 -0
  549. /package/dist/web/assets/{typespec-B7QlHMZ_.js → typespec-CBseCMtZ.js} +0 -0
  550. /package/dist/web/assets/{typst-CcHSKFZj.js → typst-4oY25U9y.js} +0 -0
  551. /package/dist/web/assets/{use-is-mobile-BXGVvczu.js → use-is-mobile-D1weanbj.js} +0 -0
  552. /package/dist/web/assets/{utils-DoY4yTP-.js → utils-BNRH507E.js} +0 -0
  553. /package/dist/web/assets/{v-xTGhWZuD.js → v-BXaZ_1oy.js} +0 -0
  554. /package/dist/web/assets/{vala-DwebjH3H.js → vala-BImWJE7z.js} +0 -0
  555. /package/dist/web/assets/{vb-BxJT2RAy.js → vb-CLfj2_sy.js} +0 -0
  556. /package/dist/web/assets/{vendor-xterm-CU6zIYMB.js → vendor-xterm-_yyu5n32.js} +0 -0
  557. /package/dist/web/assets/{verilog-CvV6RHPD.js → verilog-BWa8Ed2b.js} +0 -0
  558. /package/dist/web/assets/{vesper-CY3qd7E_.js → vesper-BGj1cg6m.js} +0 -0
  559. /package/dist/web/assets/{vhdl-B9ZX-sUL.js → vhdl-mByK7mlx.js} +0 -0
  560. /package/dist/web/assets/{viml-Y42h_ExQ.js → viml-CugmlW5g.js} +0 -0
  561. /package/dist/web/assets/{vitesse-black-DkXP6Sv-.js → vitesse-black-vPdpy9TL.js} +0 -0
  562. /package/dist/web/assets/{vitesse-dark-B9CkIB6r.js → vitesse-dark-BuQK3dUh.js} +0 -0
  563. /package/dist/web/assets/{vitesse-light-C7DHrJCK.js → vitesse-light-B3nz1XL9.js} +0 -0
  564. /package/dist/web/assets/{vyper-Cnd9s5j-.js → vyper-DH_GTIHG.js} +0 -0
  565. /package/dist/web/assets/{wasm-DQE71XTB.js → wasm-C-LF9GjX.js} +0 -0
  566. /package/dist/web/assets/{wasm-iWe7Fzjc.js → wasm-DfVbVJlW.js} +0 -0
  567. /package/dist/web/assets/{wenyan-Bc5Pnm9l.js → wenyan-CzQkHyHx.js} +0 -0
  568. /package/dist/web/assets/{wgsl-D-YUAWkP.js → wgsl-Du_M1DW9.js} +0 -0
  569. /package/dist/web/assets/{wikitext-DYQDUYf2.js → wikitext-BWN_w5S7.js} +0 -0
  570. /package/dist/web/assets/{wit-P6u7mfwy.js → wit-BR13v5HD.js} +0 -0
  571. /package/dist/web/assets/{wolfram-6SXLUqao.js → wolfram-BoXxp_Xl.js} +0 -0
  572. /package/dist/web/assets/{x-BcqfTY_7.js → x-nhriOX9o.js} +0 -0
  573. /package/dist/web/assets/{yaml-CQiMLVcZ.js → yaml-DpR_EF-d.js} +0 -0
  574. /package/dist/web/assets/{zenscript-DFMywo_P.js → zenscript-C-EdUtUd.js} +0 -0
  575. /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>
@@ -3,6 +3,7 @@ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
3
3
  import { ContextMenu as ContextMenuPrimitive } from "radix-ui"
4
4
 
5
5
  import { cn } from "@/lib/utils"
6
+ import { usePortalContainer } from "./portal-container-context"
6
7
 
7
8
  function ContextMenu({
8
9
  ...props
@@ -27,10 +28,16 @@ function ContextMenuGroup({
27
28
  }
28
29
 
29
30
  function ContextMenuPortal({
31
+ container,
30
32
  ...props
31
33
  }: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
34
+ const contextContainer = usePortalContainer()
32
35
  return (
33
- <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
36
+ <ContextMenuPrimitive.Portal
37
+ data-slot="context-menu-portal"
38
+ container={container ?? contextContainer}
39
+ {...props}
40
+ />
34
41
  )
35
42
  }
36
43
 
@@ -96,7 +103,7 @@ function ContextMenuContent({
96
103
  ...props
97
104
  }: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {
98
105
  return (
99
- <ContextMenuPrimitive.Portal>
106
+ <ContextMenuPortal>
100
107
  <ContextMenuPrimitive.Content
101
108
  data-slot="context-menu-content"
102
109
  className={cn(
@@ -105,7 +112,7 @@ function ContextMenuContent({
105
112
  )}
106
113
  {...props}
107
114
  />
108
- </ContextMenuPrimitive.Portal>
115
+ </ContextMenuPortal>
109
116
  )
110
117
  }
111
118
 
@@ -4,6 +4,7 @@ import { Dialog as DialogPrimitive } from "radix-ui"
4
4
 
5
5
  import { cn } from "@/lib/utils"
6
6
  import { Button } from "@/components/ui/button"
7
+ import { usePortalContainer } from "./portal-container-context"
7
8
 
8
9
  function Dialog({
9
10
  ...props
@@ -18,9 +19,17 @@ function DialogTrigger({
18
19
  }
19
20
 
20
21
  function DialogPortal({
22
+ container,
21
23
  ...props
22
24
  }: React.ComponentProps<typeof DialogPrimitive.Portal>) {
23
- return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
25
+ const contextContainer = usePortalContainer()
26
+ return (
27
+ <DialogPrimitive.Portal
28
+ data-slot="dialog-portal"
29
+ container={container ?? contextContainer}
30
+ {...props}
31
+ />
32
+ )
24
33
  }
25
34
 
26
35
  function DialogClose({
@@ -5,6 +5,7 @@ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
5
5
  import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
6
6
 
7
7
  import { cn } from "@/lib/utils"
8
+ import { usePortalContainer } from "./portal-container-context"
8
9
 
9
10
  function DropdownMenu({
10
11
  ...props
@@ -13,10 +14,16 @@ function DropdownMenu({
13
14
  }
14
15
 
15
16
  function DropdownMenuPortal({
17
+ container,
16
18
  ...props
17
19
  }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
20
+ const contextContainer = usePortalContainer()
18
21
  return (
19
- <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
22
+ <DropdownMenuPrimitive.Portal
23
+ data-slot="dropdown-menu-portal"
24
+ container={container ?? contextContainer}
25
+ {...props}
26
+ />
20
27
  )
21
28
  }
22
29
 
@@ -37,7 +44,7 @@ function DropdownMenuContent({
37
44
  ...props
38
45
  }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
39
46
  return (
40
- <DropdownMenuPrimitive.Portal>
47
+ <DropdownMenuPortal>
41
48
  <DropdownMenuPrimitive.Content
42
49
  data-slot="dropdown-menu-content"
43
50
  sideOffset={sideOffset}
@@ -47,7 +54,7 @@ function DropdownMenuContent({
47
54
  )}
48
55
  {...props}
49
56
  />
50
- </DropdownMenuPrimitive.Portal>
57
+ </DropdownMenuPortal>
51
58
  )
52
59
  }
53
60
 
@@ -11,6 +11,7 @@ import { createPortal } from "react-dom";
11
11
  import { cn } from "@/lib/utils";
12
12
  import { useSwipeToDismiss } from "@/hooks/use-swipe-to-dismiss";
13
13
  import { useVisualViewport } from "@/hooks/use-visual-viewport";
14
+ import { usePortalContainer } from "./portal-container-context";
14
15
 
15
16
  /* ------------------------------------------------------------------ */
16
17
  /* Core BottomSheet — reusable everywhere */
@@ -33,6 +34,7 @@ interface BottomSheetProps {
33
34
  export function BottomSheet({ open, onClose, children, className, zIndex = 50 }: BottomSheetProps) {
34
35
  const { dragY, swipeHandlers, dragStyle, backdropOpacity, isDragging } =
35
36
  useSwipeToDismiss(onClose);
37
+ const portalContainer = usePortalContainer();
36
38
 
37
39
  // Keyboard-aware geometry. The backdrop deliberately keeps covering the whole
38
40
  // layout viewport while only the panel tracks the keyboard: if the two ever
@@ -89,7 +91,7 @@ export function BottomSheet({ open, onClose, children, className, zIndex = 50 }:
89
91
  {children}
90
92
  </div>
91
93
  </div>,
92
- sheetPortalTarget(),
94
+ sheetPortalTarget(portalContainer),
93
95
  );
94
96
  }
95
97
 
@@ -103,10 +105,11 @@ export function BottomSheet({ open, onClose, children, className, zIndex = 50 }:
103
105
  * the event fired on (`body`), finds no match, and drops the event — every
104
106
  * button inside a dock tab goes dead while native listeners still fire.
105
107
  * `#root` carries no styles, so keeping the portal inside it leaves stacking
106
- * order untouched.
108
+ * order untouched. A caller-supplied `container` (`PortalContainerProvider`)
109
+ * wins over both fallbacks.
107
110
  */
108
- function sheetPortalTarget(): HTMLElement {
109
- return document.getElementById("root") ?? document.body;
111
+ function sheetPortalTarget(container?: HTMLElement): HTMLElement {
112
+ return container ?? document.getElementById("root") ?? document.body;
110
113
  }
111
114
 
112
115
  /* ------------------------------------------------------------------ */
@@ -0,0 +1,40 @@
1
+ import { createContext, useContext, type ReactNode } from "react";
2
+
3
+ /**
4
+ * Shared portal target for every Radix/portal-based primitive under
5
+ * `components/ui/`. `undefined` means "the document's default target" — Radix
6
+ * primitives fall back to `document.body`, `mobile-bottom-sheet.tsx` falls
7
+ * back to `#root`. Never pass `null`: Radix's `container` prop only applies
8
+ * its own default when the value is `undefined`, and `null` renders no
9
+ * portal at all.
10
+ *
11
+ * With no provider mounted, `usePortalContainer()` returns `undefined` and
12
+ * every primitive behaves exactly as it did before this context existed.
13
+ */
14
+ const PortalContainerContext = createContext<HTMLElement | undefined>(
15
+ undefined,
16
+ );
17
+
18
+ /**
19
+ * Mounts a portal target for all descendant Radix primitives. Pass
20
+ * `undefined` to explicitly restore the document default (e.g. once a
21
+ * picture-in-picture window closes).
22
+ */
23
+ export function PortalContainerProvider({
24
+ container,
25
+ children,
26
+ }: {
27
+ container: HTMLElement | undefined;
28
+ children: ReactNode;
29
+ }) {
30
+ return (
31
+ <PortalContainerContext.Provider value={container}>
32
+ {children}
33
+ </PortalContainerContext.Provider>
34
+ );
35
+ }
36
+
37
+ /** The caller-supplied portal container, or `undefined` outside any provider. */
38
+ export function usePortalContainer(): HTMLElement | undefined {
39
+ return useContext(PortalContainerContext);
40
+ }