@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
@@ -0,0 +1,169 @@
1
+ // Stage 2: move the live xterm tab wrapper into a Document PiP window and test it.
2
+ import { connectApp, saveResults, sleep } from "./pip-spike-common";
3
+
4
+ const TAB = "terminal:1@panel-8281c2e3";
5
+ const Q = `document.querySelector('[data-tab-pool-id="${TAB}"]')`;
6
+ const W = `window.__spike.tw`; // held reference: querySelector can't see it once adopted into the PiP doc
7
+ const T = `window.__spike.findTerminal(${W})`;
8
+ const R: Record<string, unknown> = {};
9
+ const h = await connectApp();
10
+ const { ev, cdp, sid } = h;
11
+
12
+ // Reset leftovers from an aborted run (records whether the wrapper had been pulled back while idle)
13
+ R.reset = await ev(`(() => { const S = window.__spike; const r = { hadPip: !!(S.pip && !S.pip.closed), twInPip: !!(S.tw && S.tw.ownerDocument !== document) };
14
+ if (r.twInPip) S.moveBack(S.tw); if (r.hadPip) S.pip.close(); S.pip = null; S.results = {}; return r; })()`);
15
+ await sleep(500);
16
+ await h.openViaUrl("/project/ppm/terminal/1");
17
+ await h.waitFor(`${Q} && ${Q}.style.display !== 'none' && ${Q}.querySelector('.xterm-helper-textarea')`, 15000, "terminal visible");
18
+ await ev(`window.__spike.tw = ${Q}; 'held'`);
19
+
20
+ // Baseline typing in the main document (trusted CDP keys)
21
+ await ev(`${W}.querySelector('.xterm-helper-textarea').focus(); 'f'`);
22
+ await cdp.typeText(sid, "echo SPIKE-BEFORE-PIP");
23
+ await cdp.pressKey(sid, "Enter", "Enter", 13, "\r");
24
+ await sleep(1500);
25
+ R.baseline = await ev(`(() => { const t = ${T}; return { rows: t.rows, cols: t.cols, echoed: window.__spike.termText(t, 80).includes('SPIKE-BEFORE-PIP'), renderer: ${W}.querySelectorAll('canvas').length + ' canvas' }; })()`);
26
+
27
+ // Trusted gesture: open PiP, copy styles, move wrapper
28
+ R.move = await h.runGesture("pip-terminal", `async () => {
29
+ const S = window.__spike; const w = ${W};
30
+ const pip = await documentPictureInPicture.requestWindow({ width: 900, height: 600 });
31
+ S.pip = pip;
32
+ const styles = S.copyStyles(pip.document);
33
+ const moved = S.moveToPip(pip, w);
34
+ S.installClickProbes(pip, w);
35
+ pip.addEventListener('pagehide', () => S.note('pip pagehide'));
36
+ await new Promise(r => setTimeout(r, 400));
37
+ const t = S.findTerminal(w);
38
+ const xterm = w.querySelector('.xterm'); const rect = xterm.getBoundingClientRect();
39
+ const canvases = [...w.querySelectorAll('canvas')].map(c => ({ w: c.width, h: c.height, ctx: c.getContext ? (c.__spikeCtx ?? 'n/a') : 'n/a' }));
40
+ return { styles, moved, rowsCols: { rows: t.rows, cols: t.cols }, xtermRect: { w: rect.width, h: rect.height }, canvases,
41
+ pipInner: { w: pip.innerWidth, h: pip.innerHeight }, mainHasFocus: document.hasFocus(), pipHasFocus: pip.document.hasFocus(),
42
+ themeBg: pip.getComputedStyle(w.firstElementChild.firstElementChild).backgroundColor, mainThemeBg: getComputedStyle(document.body).backgroundColor,
43
+ pipHtmlClass: pip.document.documentElement.className };
44
+ }`);
45
+ await sleep(800);
46
+
47
+ // Did ReparentingTab's layout effect pull it back yet?
48
+ R.stillInPipAfterIdle = await ev(`${W}.ownerDocument === window.__spike.pip.document`);
49
+
50
+ // Is the PiP window a CDP target?
51
+ R.newTargets = await h.newTargets();
52
+ const pipSid = await h.attachPip();
53
+ R.pipAttached = !!pipSid;
54
+ if (pipSid) R.pipTargetInfo = await cdp.eval(pipSid, `({ url: location.href, w: innerWidth, h: innerHeight, hasWrapper: !!document.querySelector('[data-tab-pool-id]'), title: document.title })`);
55
+ R.shot1 = { screen: await h.screenShot("spike-01-terminal-in-pip-screen.png"), pip: await h.pipShot(pipSid, "spike-01-terminal-in-pip.png") };
56
+
57
+ // (b) keyboard: trusted via pip target if possible, else synthetic DOM events
58
+ if (pipSid) {
59
+ await cdp.eval(pipSid, `document.querySelector('.xterm-helper-textarea').focus(); document.activeElement.className`);
60
+ await cdp.typeText(pipSid, "echo SPIKE-IN-PIP-TRUSTED");
61
+ await cdp.pressKey(pipSid, "Enter", "Enter", 13, "\r");
62
+ await sleep(1500);
63
+ R.typingTrustedInPip = await ev(`window.__spike.termText(${T}, 80).includes('SPIKE-IN-PIP-TRUSTED')`);
64
+ }
65
+ R.typingSyntheticInPip = await ev(`(async () => {
66
+ const w = ${W}; const ta = w.querySelector('.xterm-helper-textarea'); ta.focus();
67
+ const type = (s) => { for (const ch of s) { ta.dispatchEvent(new InputEvent('input', { data: ch, inputType: 'insertText', bubbles: true })); } };
68
+ type('echo SPIKE-IN-PIP-SYNTH');
69
+ ta.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', code: 'Enter', keyCode: 13, which: 13, bubbles: true }));
70
+ await new Promise(r => setTimeout(r, 1500));
71
+ return { echoed: window.__spike.termText(${T}, 80).includes('SPIKE-IN-PIP-SYNTH'), activeInPip: w.ownerDocument.activeElement === ta };
72
+ })()`);
73
+
74
+ // (c) mouse drag selection inside the PiP terminal
75
+ const selExpr = `(() => { const t = ${T}; return t.getSelection(); })()`;
76
+ if (pipSid) {
77
+ const rect = await cdp.eval<any>(pipSid, `(() => { const r = document.querySelector('.xterm-screen').getBoundingClientRect(); return { x: r.x, y: r.y, w: r.width, h: r.height }; })()`);
78
+ const y = rect.y + 8; // first row: prompt line
79
+ await cdp.send("Input.dispatchMouseEvent", { type: "mousePressed", x: rect.x + 4, y, button: "left", clickCount: 1 }, pipSid);
80
+ for (let i = 1; i <= 6; i++) await cdp.send("Input.dispatchMouseEvent", { type: "mouseMoved", x: rect.x + 4 + i * 40, y, button: "left", buttons: 1 }, pipSid);
81
+ await cdp.send("Input.dispatchMouseEvent", { type: "mouseReleased", x: rect.x + 244, y, button: "left", clickCount: 1 }, pipSid);
82
+ await sleep(300);
83
+ R.dragSelectTrustedInPip = { selection: await ev(selExpr), rect };
84
+ await ev(`${T}.clearSelection(); 'c'`);
85
+ }
86
+ R.dragSelectSyntheticInPip = await ev(`(async () => {
87
+ const w = ${W}; const t = ${T}; const pipDoc = w.ownerDocument; const screen = w.querySelector('.xterm-screen'); const r = screen.getBoundingClientRect();
88
+ const mk = (type, x, target, extra = {}) => target.dispatchEvent(new MouseEvent(type, { clientX: x, clientY: r.y + 8, button: 0, buttons: 1, bubbles: true, view: pipDoc.defaultView, ...extra }));
89
+ mk('mousedown', r.x + 4, screen);
90
+ for (let i = 1; i <= 6; i++) mk('mousemove', r.x + 4 + i * 40, pipDoc); // xterm listens on ownerDocument-at-open-time
91
+ mk('mouseup', r.x + 244, pipDoc);
92
+ await new Promise(res => setTimeout(res, 200));
93
+ const viaPipDoc = t.getSelection(); t.clearSelection();
94
+ mk('mousedown', r.x + 4, screen);
95
+ for (let i = 1; i <= 6; i++) mk('mousemove', r.x + 4 + i * 40, document);
96
+ mk('mouseup', r.x + 244, document);
97
+ await new Promise(res => setTimeout(res, 200));
98
+ const viaMainDoc = t.getSelection(); t.clearSelection();
99
+ return { viaPipDoc, viaMainDoc };
100
+ })()`);
101
+
102
+ // (d) resize: shrink wrapper height in the PiP body → ResizeObserver/fit?
103
+ R.resizeViaStyle = await ev(`(async () => {
104
+ const w = ${W}; const t = ${T}; const before = { rows: t.rows, cols: t.cols };
105
+ w.style.height = '50%'; w.style.width = '70%';
106
+ await new Promise(r => setTimeout(r, 700));
107
+ const after = { rows: t.rows, cols: t.cols };
108
+ w.style.height = '100%'; w.style.width = '100%';
109
+ await new Promise(r => setTimeout(r, 700));
110
+ const restored = { rows: t.rows, cols: t.cols };
111
+ return { before, after, restored, refit: before.rows !== after.rows || before.cols !== after.cols };
112
+ })()`);
113
+ R.resizeViaWindow = await ev(`(async () => {
114
+ const pip = window.__spike.pip; const t = ${T}; const before = { rows: t.rows, cols: t.cols, w: pip.innerWidth, h: pip.innerHeight };
115
+ let err = null; try { pip.resizeTo(1100, 750); } catch (e) { err = String(e); }
116
+ await new Promise(r => setTimeout(r, 900));
117
+ return { before, after: { rows: t.rows, cols: t.cols, w: pip.innerWidth, h: pip.innerHeight }, err };
118
+ })()`);
119
+
120
+ // (f) React onClick inside the moved wrapper: the status-bar "Chat" button (toast in main doc on success)
121
+ R.reactClick = await ev(`(async () => {
122
+ const S = window.__spike; const w = ${W}; const btn = w.querySelector('button[title="Send to Chat"]');
123
+ const probeBefore = { ...S.probe }; btn.click();
124
+ await new Promise(r => setTimeout(r, 600));
125
+ return { probeBefore, probeAfter: { ...S.probe }, toast: S.toastText(), synthetic: true };
126
+ })()`);
127
+ if (pipSid) {
128
+ const pt = await cdp.eval<any>(pipSid, `(() => { const r = document.querySelector('button[title="Send to Chat"]').getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; })()`);
129
+ await cdp.click(pipSid, pt.x, pt.y);
130
+ await sleep(600);
131
+ R.reactClickTrusted = await ev(`({ probe: { ...window.__spike.probe }, toast: window.__spike.toastText() })`);
132
+ }
133
+ // Control: same button after moving back must fire (proves probe validity)
134
+
135
+ // Does a main-window re-render pull the wrapper back? Click the "Terminal 1" tab header in main doc.
136
+ R.pullbackProbe = await ev(`(async () => {
137
+ const w = ${W}; const inPip0 = w.ownerDocument !== document;
138
+ const tabBtn = [...document.querySelectorAll('[role="tab"], button, div')].find(e => e.textContent?.trim() === 'Terminal 1' && e.getBoundingClientRect().width > 0);
139
+ tabBtn?.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })); tabBtn?.click();
140
+ await new Promise(r => setTimeout(r, 500));
141
+ return { inPipBefore: inPip0, inPipAfterTabClick: w.ownerDocument !== document, found: !!tabBtn };
142
+ })()`);
143
+ R.shot2 = await h.pipShot(pipSid, "spike-01b-terminal-pip-after-resize.png");
144
+
145
+ // Move back to the original slot, verify state + typing
146
+ R.back = await ev(`(async () => {
147
+ const S = window.__spike; const w = ${W}; const t = ${T};
148
+ const wasInPip = w.ownerDocument !== document;
149
+ const back = wasInPip ? S.moveBack(w) : { back: true, note: 'already pulled back' };
150
+ await new Promise(r => setTimeout(r, 800));
151
+ const txt = S.termText(t, 120);
152
+ return { wasInPip, back, rows: t.rows, cols: t.cols, scrollbackKept: { before: txt.includes('SPIKE-BEFORE-PIP'), trusted: txt.includes('SPIKE-IN-PIP-TRUSTED'), synth: txt.includes('SPIKE-IN-PIP-SYNTH') } };
153
+ })()`);
154
+ await ev(`window.__spike.pip && window.__spike.pip.close(); 'closed'`);
155
+ await sleep(500);
156
+ await ev(`${W}.querySelector('.xterm-helper-textarea').focus(); 'f'`);
157
+ await cdp.typeText(sid, "echo SPIKE-AFTER-RETURN");
158
+ await cdp.pressKey(sid, "Enter", "Enter", 13, "\r");
159
+ await sleep(1500);
160
+ R.afterReturn = await ev(`(async () => {
161
+ const S = window.__spike; const w = ${W}; const t = ${T};
162
+ const btn = w.querySelector('button[title="Send to Chat"]'); const p0 = { ...S.probe }; btn.click();
163
+ await new Promise(r => setTimeout(r, 600));
164
+ return { echoed: S.termText(t, 80).includes('SPIKE-AFTER-RETURN'), rows: t.rows, cols: t.cols, reactClickControl: { probeBefore: p0, probeAfter: { ...S.probe }, toast: S.toastText() } };
165
+ })()`);
166
+ await cdp.screenshot(sid, "C:/Users/PC/ppm/plans/reports/screenshots/spike-03-terminal-after-return.png");
167
+ R.log = await ev(`window.__spike.log`);
168
+ await saveResults("pip-results-terminal.json", R);
169
+ cdp.close();
@@ -0,0 +1,36 @@
1
+ // Debug: trace xterm's internal key path on the round-tripped terminal vs the fresh one.
2
+ import { connectApp, saveResults, sleep } from "./pip-spike-common";
3
+
4
+ const R: Record<string, unknown> = {};
5
+ const h = await connectApp();
6
+ const { ev, cdp, sid } = h;
7
+ await cdp.send("Emulation.setFocusEmulationEnabled", { enabled: true }, sid);
8
+
9
+ const TRACE = `(t, name) => {
10
+ const S = window.__spike; const core = t._core; S.trace = S.trace || {}; S.trace[name] = [];
11
+ const log = (m) => S.trace[name].push(m);
12
+ const wrap = (obj, key, label) => { if (!obj || typeof obj[key] !== 'function' || obj[key].__wrapped) return 'no:' + label; const o = obj[key];
13
+ const w = function (...a) { let r; try { r = o.apply(this, a); } finally { log(label + ' -> ' + (r && typeof r === 'object' ? JSON.stringify(r) : String(r))); } return r; }; w.__wrapped = true; obj[key] = w; return 'ok:' + label; };
14
+ const res = [
15
+ wrap(core, '_keyDown', 'keyDown'), wrap(core, '_keyPress', 'keyPress'), wrap(core, '_inputEvent', 'inputEvent'), wrap(core, '_isThirdLevelShift', 'thirdLevel'),
16
+ wrap(core._compositionHelper, 'keydown', 'composition.keydown'), wrap(core._keyboardService, 'evaluateKeyDown', 'evaluateKeyDown'), wrap(core.coreService, 'triggerDataEvent', 'triggerData'),
17
+ ];
18
+ return { wrapped: res, state: { unprocessedDeadKey: core._unprocessedDeadKey, keyDownHandled: core._keyDownHandled, keyPressHandled: core._keyPressHandled, isComposing: core._compositionHelper._isComposing, isSending: core._compositionHelper._isSendingComposition,
19
+ useKitty: core._keyboardService.useKitty, useWin32: core._keyboardService.useWin32InputMode, customHandler: !!core._customKeyEventHandler, disableStdin: core.optionsService.rawOptions.disableStdin, screenReader: core.optionsService.rawOptions.screenReaderMode,
20
+ textareaOwner: t.textarea.ownerDocument === document, browser: { isWindows: core.browser.isWindows, isMac: core.browser.isMac } } };
21
+ }`;
22
+
23
+ async function traceOne(label: string, tabPath: string, twExpr: string, tExpr: string) {
24
+ await h.openViaUrl(tabPath);
25
+ await sleep(500);
26
+ R[label + "_setup"] = await ev(`(${TRACE})(${tExpr}, ${JSON.stringify(label)})`);
27
+ await ev(`${twExpr}.querySelector('.xterm-helper-textarea').focus(); window.__spike.trace[${JSON.stringify(label)}] = []; 'f'`);
28
+ await cdp.typeText(sid, "a");
29
+ await cdp.pressKey(sid, "Enter", "Enter", 13, "\r");
30
+ await sleep(400);
31
+ R[label + "_trace"] = await ev(`window.__spike.trace[${JSON.stringify(label)}]`);
32
+ }
33
+ await traceOne("roundTripped", "/project/ppm/terminal/1", "window.__spike.tw", "window.__spike.findTerminal(window.__spike.tw)");
34
+ await traceOne("fresh", "/project/ppm/terminal/2", "window.__spike.tw2", "window.__spike.t2");
35
+ await saveResults("pip-results-xterm-trace.json", R);
36
+ cdp.close();
@@ -0,0 +1,67 @@
1
+ // Isolates the `useWindowDrag` memo console error: opens an explorer window and clicks a
2
+ // caption button, with NO picture-in-picture involved at all.
3
+ import { tmpdir, homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { Database } from "bun:sqlite";
6
+ import { Cdp, launchChrome, pageInitScript, sleep } from "../tests/e2e/tab-popout-pip-helpers.mjs";
7
+
8
+ const WEB = "http://localhost:5199";
9
+ const CDP_PORT = 9361;
10
+ const CHROME = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";
11
+ const RUN = Date.now().toString(36);
12
+ const PROFILE = join(tmpdir(), `ppm-probe-${RUN}`);
13
+
14
+ const db = new Database(join(homedir(), ".ppm", "ppm.dev.db"), { readonly: true });
15
+ const TOKEN = JSON.parse(db.query("SELECT value FROM config WHERE key='auth'").get().value).token;
16
+ db.close();
17
+
18
+ const WINDOWS = `(await import('/components/floating-window/window-store.ts')).useWindowStore`;
19
+ const errors = [];
20
+ let cdp, main, chrome;
21
+
22
+ try {
23
+ chrome = await launchChrome({ chromePath: CHROME, cdpPort: CDP_PORT, profileDir: PROFILE, url: "about:blank" });
24
+ cdp = await Cdp.connect(CDP_PORT);
25
+ let page = null;
26
+ const end = Date.now() + 20000;
27
+ while (Date.now() < end && !page) {
28
+ page = (await cdp.targets()).find((t) => t.type === "page");
29
+ if (!page) await sleep(300);
30
+ }
31
+ main = await cdp.attach(page.targetId);
32
+ await cdp.send("Emulation.setFocusEmulationEnabled", { enabled: true }, main).catch(() => {});
33
+ await cdp.send("Page.addScriptToEvaluateOnNewDocument", { source: pageInitScript(TOKEN) }, main);
34
+ cdp.on((m) => {
35
+ if (m.method === "Runtime.exceptionThrown") {
36
+ errors.push(String(m.params?.exceptionDetails?.exception?.description ?? "").split("\n")[0]);
37
+ }
38
+ });
39
+ await cdp.send("Page.navigate", { url: WEB }, main);
40
+ await sleep(6000);
41
+
42
+ const ev = (e) => cdp.evalJs(main, e);
43
+ for (let i = 0; i < 3; i++) {
44
+ await ev(`${WINDOWS}.getState().open('explorer')`);
45
+ await sleep(1800);
46
+ const json = await ev(`(() => {
47
+ const el = document.querySelector('[aria-label="Close window"]');
48
+ if (!el) return null;
49
+ const r = el.getBoundingClientRect();
50
+ return JSON.stringify({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
51
+ })()`);
52
+ if (!json) throw new Error("no close button");
53
+ const { x, y } = JSON.parse(json);
54
+ await cdp.click(main, x, y);
55
+ await sleep(1200);
56
+ }
57
+ console.log(`close-button-only clicks: ${errors.length} exception(s)`);
58
+ for (const e of errors.slice(0, 3)) console.log(" " + e);
59
+ } finally {
60
+ try {
61
+ await cdp?.send("Browser.close");
62
+ } catch {}
63
+ await sleep(1000);
64
+ try {
65
+ if (chrome?.pid) process.kill(chrome.pid);
66
+ } catch {}
67
+ }
@@ -0,0 +1,15 @@
1
+ // Read auth token + projects from the dev DB (readonly). Spike helper only.
2
+ import { Database } from "bun:sqlite";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+
6
+ const db = new Database(join(homedir(), ".ppm", "ppm.dev.db"), { readonly: true });
7
+ const auth = db.query("SELECT value FROM config WHERE key = 'auth'").get() as { value: string } | null;
8
+ const tables = db.query("SELECT name FROM sqlite_master WHERE type='table'").all() as { name: string }[];
9
+ let projects: unknown[] = [];
10
+ try {
11
+ projects = db.query("SELECT * FROM projects LIMIT 10").all();
12
+ } catch (e) {
13
+ projects = [String(e)];
14
+ }
15
+ console.log(JSON.stringify({ auth: auth ? JSON.parse(auth.value) : null, tables: tables.map((t) => t.name), projects }, null, 2));
@@ -0,0 +1,7 @@
1
+ MAIN1 MAIN1 // Scratch file for the Document PiP spike. Monaco autosaves edits here; safe to delete.
2
+ export const spikeLine1 = "one";
3
+ export const spikeLine2 = "two";
4
+ export const spikeLine3 = "three";
5
+ export function spikeFn(a: number, b: number): number {
6
+ return a + b;
7
+ }
@@ -0,0 +1,353 @@
1
+ // Throwaway manual smoke for the unified window chrome + frame-owned PiP.
2
+ // Not part of the test suite; lives in .spike/ (untracked).
3
+ // bun .spike/smoke-unified-chrome-pip.mjs
4
+ import { tmpdir, homedir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { mkdir, rm, writeFile } from "node:fs/promises";
7
+ import { Database } from "bun:sqlite";
8
+ import { Cdp, launchChrome, pageInitScript, sleep } from "../tests/e2e/tab-popout-pip-helpers.mjs";
9
+
10
+ const WEB = "http://localhost:5199";
11
+ const API = "http://127.0.0.1:8099";
12
+ const CDP_PORT = 9357;
13
+ const CHROME = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";
14
+ const RUN = Date.now().toString(36);
15
+ const PROFILE = join(tmpdir(), `ppm-smoke-${RUN}`);
16
+ const PROJECT_DIR = join(tmpdir(), `ppm-smoke-proj-${RUN}`);
17
+ const PROJECT_NAME = `smoke-pip-${RUN}`;
18
+
19
+ const db = new Database(join(homedir(), ".ppm", "ppm.dev.db"), { readonly: true });
20
+ const TOKEN = JSON.parse(db.query("SELECT value FROM config WHERE key='auth'").get().value).token;
21
+ db.close();
22
+
23
+ const out = [];
24
+ const rec = (name, pass, detail = "") => {
25
+ out.push({ name, pass, detail });
26
+ console.log(`[${pass ? "PASS" : "FAIL"}] ${name}${detail ? " — " + detail : ""}`);
27
+ };
28
+
29
+ let cdp, main, mainTargetId, chrome;
30
+ const ev = (e, ms) => cdp.evalJs(main, e, ms);
31
+ const WINDOWS = `(await import('/components/floating-window/window-store.ts')).useWindowStore`;
32
+ const PANELS = `(await import('/stores/panel-store.ts')).usePanelStore`;
33
+ const PIPREG = `(await import('/components/floating-window/window-pip-registry.ts'))`;
34
+
35
+ async function waitFor(expr, ms = 25000, label = expr) {
36
+ const end = Date.now() + ms;
37
+ let last;
38
+ while (Date.now() < end) {
39
+ try {
40
+ last = await ev(`!!(${expr})`, 8000);
41
+ if (last) return true;
42
+ } catch (e) {
43
+ last = e.message;
44
+ }
45
+ await sleep(250);
46
+ }
47
+ throw new Error(`timeout: ${label} (last=${last})`);
48
+ }
49
+
50
+ async function clickSel(selectorExpr, label, sid = main) {
51
+ const json = await cdp.evalJs(
52
+ sid,
53
+ `(() => { const el = ${selectorExpr}; if (!el) return null; const r = el.getBoundingClientRect();
54
+ return JSON.stringify({ x: r.x + r.width / 2, y: r.y + r.height / 2 }); })()`,
55
+ );
56
+ if (!json) throw new Error(`not found: ${label}`);
57
+ const { x, y } = JSON.parse(json);
58
+ await cdp.click(sid, x, y);
59
+ }
60
+
61
+ const pipBtn = (winLabelIdx = 0) =>
62
+ `[...document.querySelectorAll('[aria-label="Open in picture-in-picture"]')][${winLabelIdx}]`;
63
+
64
+ async function findPipTarget() {
65
+ const t = await cdp.targets();
66
+ return t.find((x) => x.type === "page" && x.openerId === mainTargetId && x.targetId !== mainTargetId) ?? null;
67
+ }
68
+
69
+ async function waitPipTarget(open = true, ms = 15000) {
70
+ const end = Date.now() + ms;
71
+ while (Date.now() < end) {
72
+ await sleep(300);
73
+ const t = await findPipTarget();
74
+ if (open && t) return t;
75
+ if (!open && !t) return null;
76
+ }
77
+ throw new Error(open ? "PiP never opened" : "PiP never closed");
78
+ }
79
+
80
+ const errors = [];
81
+
82
+ async function api(method, path, body) {
83
+ const r = await fetch(`${API}${path}`, {
84
+ method,
85
+ headers: { Authorization: `Bearer ${TOKEN}`, "Content-Type": "application/json" },
86
+ body: body ? JSON.stringify(body) : undefined,
87
+ });
88
+ return r.json();
89
+ }
90
+
91
+ /** Real double-click: a second press with clickCount 2 is what makes Chrome emit `dblclick`. */
92
+ async function dblclick(sid, x, y) {
93
+ await cdp.send("Input.dispatchMouseEvent", { type: "mouseMoved", x, y }, sid);
94
+ for (const clickCount of [1, 2]) {
95
+ const base = { x, y, button: "left", clickCount };
96
+ await cdp.send("Input.dispatchMouseEvent", { ...base, type: "mousePressed" }, sid);
97
+ await cdp.send("Input.dispatchMouseEvent", { ...base, type: "mouseReleased" }, sid);
98
+ }
99
+ }
100
+
101
+ /** `use-terminal` hard-codes ws://<host>:8081 for http dev; this dev stack is on 8099. */
102
+ async function retargetTerminalWs(sid, port) {
103
+ await cdp.send("Network.enable", {}, sid).catch(() => {});
104
+ await cdp.send("Network.setCacheDisabled", { cacheDisabled: true }, sid).catch(() => {});
105
+ await cdp.send("Fetch.enable", { patterns: [{ urlPattern: "*/hooks/use-terminal.ts*", requestStage: "Response" }] }, sid);
106
+ cdp.on(async (m) => {
107
+ if (m.method !== "Fetch.requestPaused" || m.sessionId !== sid) return;
108
+ const { requestId } = m.params;
109
+ try {
110
+ const r = await cdp.send("Fetch.getResponseBody", { requestId }, sid);
111
+ const text = (r.base64Encoded ? Buffer.from(r.body, "base64").toString("utf8") : r.body).replace(
112
+ /hostname\}:8081/g,
113
+ `hostname}:${port}`,
114
+ );
115
+ await cdp.send("Fetch.fulfillRequest", {
116
+ requestId,
117
+ responseCode: 200,
118
+ responseHeaders: [
119
+ { name: "content-type", value: "application/javascript" },
120
+ { name: "cache-control", value: "no-store" },
121
+ ],
122
+ body: Buffer.from(text, "utf8").toString("base64"),
123
+ }, sid);
124
+ } catch {
125
+ await cdp.send("Fetch.continueRequest", { requestId }, sid).catch(() => {});
126
+ }
127
+ });
128
+ }
129
+
130
+ async function main_() {
131
+ await mkdir(PROJECT_DIR, { recursive: true });
132
+ await writeFile(join(PROJECT_DIR, "scratch.ts"), "export const scratch = 1;\n", "utf8");
133
+ const created = await api("POST", "/api/projects", { path: PROJECT_DIR, name: PROJECT_NAME });
134
+ if (!created?.ok && !created?.data) throw new Error(`temp project not created: ${JSON.stringify(created)}`);
135
+ console.log(`temp project ${PROJECT_NAME} → ${PROJECT_DIR}`);
136
+
137
+ chrome = await launchChrome({ chromePath: CHROME, cdpPort: CDP_PORT, profileDir: PROFILE, url: "about:blank" });
138
+ cdp = await Cdp.connect(CDP_PORT);
139
+ const end = Date.now() + 20000;
140
+ let page = null;
141
+ while (Date.now() < end && !page) {
142
+ page = (await cdp.targets()).find((t) => t.type === "page");
143
+ if (!page) await sleep(300);
144
+ }
145
+ mainTargetId = page.targetId;
146
+ main = await cdp.attach(mainTargetId);
147
+ await cdp.send("Emulation.setFocusEmulationEnabled", { enabled: true }, main).catch(() => {});
148
+ await cdp.send("Page.addScriptToEvaluateOnNewDocument", { source: pageInitScript(TOKEN) }, main);
149
+ cdp.on((m) => {
150
+ if (m.method === "Runtime.consoleAPICalled" && m.params?.type === "error") {
151
+ errors.push(JSON.stringify(m.params.args?.map((a) => a.value ?? a.description)).slice(0, 300));
152
+ }
153
+ if (m.method === "Runtime.exceptionThrown") {
154
+ errors.push(String(m.params?.exceptionDetails?.exception?.description ?? "").slice(0, 300));
155
+ }
156
+ });
157
+ await retargetTerminalWs(main, "8099");
158
+ await cdp.send("Page.navigate", { url: `${WEB}/project/${PROJECT_NAME}` }, main);
159
+ await sleep(4000);
160
+ await waitFor(
161
+ `(await import('/stores/project-store.ts')).useProjectStore.getState().activeProject?.name === ${JSON.stringify(PROJECT_NAME)}`,
162
+ 40000,
163
+ "temp project active",
164
+ );
165
+ await waitFor(`${PANELS}.getState().grid.flat().length > 0`, 20000, "grid ready");
166
+ await sleep(1500);
167
+
168
+ // ---- 1. explorer window ----
169
+ const expId = await ev(`${WINDOWS}.getState().open('explorer')`);
170
+ await sleep(2000);
171
+ const expChrome = JSON.parse(await ev(`(async () => {
172
+ const win = document.querySelector('[role="group"][aria-roledescription="window"]');
173
+ const bar = win?.querySelector('[data-skin]');
174
+ return JSON.stringify({
175
+ skin: bar?.getAttribute('data-skin') ?? null,
176
+ pip: !!win?.querySelector('[aria-label="Open in picture-in-picture"]'),
177
+ folderIcon: !!bar?.querySelector('svg'),
178
+ });
179
+ })()`));
180
+ rec("explorer window wears a skinned titlebar with a PiP button", !!expChrome.skin && expChrome.pip, JSON.stringify(expChrome));
181
+
182
+ // ---- 2. explorer PiP: React events alive (folder click navigates) ----
183
+ await clickSel(pipBtn(), "explorer pip button");
184
+ const pipT = await waitPipTarget(true);
185
+ const pipSid = await cdp.attach(pipT.targetId);
186
+ await cdp.send("Emulation.setFocusEmulationEnabled", { enabled: true }, pipSid).catch(() => {});
187
+ await sleep(1200);
188
+ const inPip = await ev(`!!(${PIPREG}.windowPip(${JSON.stringify(expId)}))`);
189
+ const placeholder = await ev(`document.body.innerText.includes('Playing in picture-in-picture')`);
190
+ rec("explorer body adopted into PiP + frame shows the placeholder", inPip && placeholder, `pip=${inPip} placeholder=${placeholder}`);
191
+
192
+ // Double-click a directory row inside the PiP document (explorer opens folders on dblclick).
193
+ const currentPath = () =>
194
+ ev(`(await import('/components/os-explorer/explorer-store.ts')).useExplorerStore.getState().slices[${JSON.stringify(expId)}]?.path ?? null`);
195
+ const pathBefore = await currentPath();
196
+ const target = await ev(`(async () => {
197
+ const st = (await import('/components/os-explorer/explorer-store.ts')).useExplorerStore.getState();
198
+ const slice = st.slices[${JSON.stringify(expId)}];
199
+ const dir = (slice?.entries ?? []).find(e => e.type === 'directory');
200
+ return dir ? dir.path : null;
201
+ })()`);
202
+ const navigated = await (async () => {
203
+ if (!target) return "no-directory-entry";
204
+ const json = await cdp.evalJs(pipSid, `(() => {
205
+ const el = document.querySelector('[data-testid="explorer-row"][data-path=' + JSON.stringify(${JSON.stringify(target)}) + ']');
206
+ if (!el) return null;
207
+ const r = el.getBoundingClientRect();
208
+ return JSON.stringify({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
209
+ })()`);
210
+ if (!json) return "row-not-in-pip-doc";
211
+ const { x, y } = JSON.parse(json);
212
+ await dblclick(pipSid, x, y);
213
+ await sleep(2500);
214
+ const after = await currentPath();
215
+ return after && after !== pathBefore ? "ok" : `unchanged (${pathBefore} → ${after})`;
216
+ })();
217
+ rec("clicking a folder inside the PiP explorer navigates (React events alive)", navigated === "ok", `${navigated} target=${target}`);
218
+
219
+ // ---- 3. close PiP restores ----
220
+ await clickSel(`document.querySelector('[aria-label="Bring back from picture-in-picture"]')`, "bring back");
221
+ await waitPipTarget(false);
222
+ const restored = await ev(`(async () => {
223
+ const win = document.querySelector('[role="group"][aria-roledescription="window"]');
224
+ return !!win && !document.body.innerText.includes('Playing in picture-in-picture') && win.children.length > 1;
225
+ })()`);
226
+ rec("closing PiP restores the explorer body into its window", restored, `restored=${restored}`);
227
+ await ev(`${WINDOWS}.getState().close(${JSON.stringify(expId)})`);
228
+ await sleep(600);
229
+
230
+ // ---- 4. system monitor window ----
231
+ const sysId = await ev(`${WINDOWS}.getState().open('system-monitor')`);
232
+ await sleep(2500);
233
+ const sysChrome = JSON.parse(await ev(`(async () => {
234
+ const win = document.querySelector('[role="group"][aria-roledescription="window"]');
235
+ const bar = win?.querySelector('[data-skin]');
236
+ return JSON.stringify({ skin: bar?.getAttribute('data-skin') ?? null, pip: !!win?.querySelector('[aria-label="Open in picture-in-picture"]') });
237
+ })()`));
238
+ rec("system monitor window has the same skin + a PiP button", !!sysChrome.skin && sysChrome.pip, JSON.stringify(sysChrome));
239
+ await clickSel(pipBtn(), "sysmon pip button");
240
+ const sysPip = await waitPipTarget(true);
241
+ const sysSid = await cdp.attach(sysPip.targetId);
242
+ await sleep(1500);
243
+ const sysAlive = await cdp.evalJs(sysSid, `document.body.innerText.length > 40`);
244
+ rec("system monitor renders inside PiP", !!sysAlive, `textLen>40=${sysAlive}`);
245
+ await clickSel(`document.querySelector('[aria-label="Bring back from picture-in-picture"]')`, "bring back sysmon");
246
+ await waitPipTarget(false);
247
+ await ev(`${WINDOWS}.getState().close(${JSON.stringify(sysId)})`);
248
+ await sleep(600);
249
+
250
+ // ---- 5. terminal tab pop-out ----
251
+ const TERM = await ev(`${PANELS}.getState().openTab({ type: 'terminal', title: 'Terminal 1', projectId: ${JSON.stringify(PROJECT_NAME)}, closable: true, metadata: { terminalIndex: 1, projectName: ${JSON.stringify(PROJECT_NAME)} } })`);
252
+ const sel = (extra = "") => `document.querySelector('[data-tab-pool-id=' + ${JSON.stringify(JSON.stringify(TERM))} + ']${extra}')`;
253
+ await waitFor(sel(" .xterm"), 40000, "xterm mounted");
254
+ await sleep(2000);
255
+ const winId = await ev(`(async () => {
256
+ const p = ${PANELS}.getState();
257
+ const pid = Object.keys(p.panels).find(id => p.panels[id].tabs.some(t => t.id === ${JSON.stringify(TERM)}));
258
+ return p.popOutTab(${JSON.stringify(TERM)}, pid);
259
+ })()`);
260
+ await sleep(1800);
261
+ const tabChrome = JSON.parse(await ev(`(async () => {
262
+ const win = document.querySelector('[role="group"][aria-roledescription="window"]');
263
+ const bar = win?.querySelector('[data-skin]');
264
+ return JSON.stringify({
265
+ skin: bar?.getAttribute('data-skin') ?? null,
266
+ pip: !!win?.querySelector('[aria-label="Open in picture-in-picture"]'),
267
+ });
268
+ })()`));
269
+ rec(
270
+ "popped-out tab window wears the SAME skin as the explorer",
271
+ tabChrome.skin === expChrome.skin && tabChrome.pip,
272
+ `tab=${JSON.stringify(tabChrome)} explorer=${JSON.stringify(expChrome)}`,
273
+ );
274
+
275
+ await clickSel(pipBtn(), "tab pip button");
276
+ const tPip = await waitPipTarget(true);
277
+ const tSid = await cdp.attach(tPip.targetId);
278
+ await cdp.send("Emulation.setFocusEmulationEnabled", { enabled: true }, tSid).catch(() => {});
279
+ await sleep(1500);
280
+ const termInPip = await cdp.evalJs(tSid, `!!${sel(" .xterm")}`);
281
+ rec("terminal tab lives in the PiP document", !!termInPip, `xterm=${termInPip}`);
282
+
283
+ // Close the window WHILE in PiP → must restore and re-dock.
284
+ await clickSel(`document.querySelector('[aria-label="Close window"]')`, "close window");
285
+ await waitPipTarget(false);
286
+ await sleep(1500);
287
+ const redocked = JSON.parse(await ev(`(async () => {
288
+ const p = ${PANELS}.getState();
289
+ const pid = Object.keys(p.panels).find(id => p.panels[id].tabs.some(t => t.id === ${JSON.stringify(TERM)}));
290
+ return JSON.stringify({
291
+ panel: pid ?? null,
292
+ gridded: !!pid && p.grid.flat().includes(pid),
293
+ inMainDoc: !!${sel()},
294
+ windows: Object.keys(${WINDOWS}.getState().windows).length,
295
+ });
296
+ })()`));
297
+ rec(
298
+ "closing the window while in PiP restores + re-docks the tab",
299
+ !!redocked.panel && redocked.gridded && redocked.inMainDoc && redocked.windows === 0,
300
+ JSON.stringify(redocked),
301
+ );
302
+
303
+ // ---- 6. the other skin: folder glyph is explorer-only ----
304
+ await ev(`(await import('/stores/settings-store.ts')).useSettingsStore.setState({ explorerSkin: 'windows' })`);
305
+ const winSkinExp = await ev(`${WINDOWS}.getState().open('explorer')`);
306
+ await sleep(1800);
307
+ const winSkinSys = await ev(`${WINDOWS}.getState().open('system-monitor')`);
308
+ await sleep(2200);
309
+ const skinCheck = JSON.parse(await ev(`(async () => {
310
+ const wins =[...document.querySelectorAll('[role="group"][aria-roledescription="window"]')].map(w => {
311
+ const bar = w.querySelector('[data-skin]');
312
+ return {
313
+ label: w.getAttribute('aria-label'),
314
+ skin: bar?.getAttribute('data-skin') ?? null,
315
+ icon: !!bar?.querySelector('div > svg'),
316
+ pip: !!w.querySelector('[aria-label="Open in picture-in-picture"]'),
317
+ };
318
+ });
319
+ return JSON.stringify(wins);
320
+ })()`));
321
+ const sysWin = skinCheck.find((w) => w.label === "System Monitor");
322
+ const expWin = skinCheck.find((w) => w.label !== "System Monitor");
323
+ rec(
324
+ "windows skin: both kinds skinned + PiP, folder glyph only on the explorer",
325
+ skinCheck.every((w) => w.skin === "windows" && w.pip) && expWin?.icon === true && sysWin?.icon === false,
326
+ JSON.stringify(skinCheck),
327
+ );
328
+ await ev(`(async () => { const w = ${WINDOWS}.getState(); w.close(${JSON.stringify(winSkinExp)}); w.close(${JSON.stringify(winSkinSys)}); return 'ok'; })()`);
329
+ await sleep(600);
330
+
331
+ rec("no console errors during the smoke", errors.length === 0, errors.slice(0, 4).join(" || "));
332
+ }
333
+
334
+ try {
335
+ await main_();
336
+ } catch (e) {
337
+ rec("smoke aborted", false, e?.message ?? String(e));
338
+ } finally {
339
+ console.log(`\nPID ${chrome?.pid} — closing`);
340
+ try {
341
+ await cdp?.send("Browser.close");
342
+ } catch {}
343
+ await sleep(1200);
344
+ try {
345
+ if (chrome?.pid) process.kill(chrome.pid);
346
+ } catch {}
347
+ const removed = await api("DELETE", `/api/projects/${PROJECT_NAME}`).catch((e) => ({ error: String(e) }));
348
+ console.log(`temp project removed: ${JSON.stringify(removed)}`);
349
+ await rm(PROJECT_DIR, { recursive: true, force: true }).catch(() => {});
350
+ const failed = out.filter((r) => !r.pass);
351
+ console.log(`\n${out.length - failed.length}/${out.length} passed`);
352
+ process.exit(failed.length ? 1 : 0);
353
+ }