@hienlh/ppm 0.18.9 → 0.18.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.spike/cdp-client.ts +116 -0
- package/.spike/cleanup-ppm-workspace.mjs +44 -0
- package/.spike/group-protected-probe.ts +21 -0
- package/.spike/pip-eval.ts +17 -0
- package/.spike/pip-page-helpers.js +119 -0
- package/.spike/pip-results-editor-glue.json +105 -0
- package/.spike/pip-results-editor.json +158 -0
- package/.spike/pip-results-input-isolation.json +28 -0
- package/.spike/pip-results-key-probe.json +58 -0
- package/.spike/pip-results-listener-survival.json +98 -0
- package/.spike/pip-results-listeners.json +36 -0
- package/.spike/pip-results-pagehide.json +92 -0
- package/.spike/pip-results-resize.json +104 -0
- package/.spike/pip-results-terminal-debug.json +99 -0
- package/.spike/pip-results-terminal-v2.json +191 -0
- package/.spike/pip-results-terminal.json +220 -0
- package/.spike/pip-results-xterm-trace.json +73 -0
- package/.spike/pip-spike-common.ts +83 -0
- package/.spike/pip-spike-editor-glue.ts +82 -0
- package/.spike/pip-spike-editor.ts +129 -0
- package/.spike/pip-spike-input-isolation.ts +38 -0
- package/.spike/pip-spike-key-probe.ts +34 -0
- package/.spike/pip-spike-listener-survival.ts +54 -0
- package/.spike/pip-spike-listeners.ts +19 -0
- package/.spike/pip-spike-pagehide.ts +59 -0
- package/.spike/pip-spike-resize.ts +42 -0
- package/.spike/pip-spike-setup.ts +63 -0
- package/.spike/pip-spike-terminal-debug.ts +81 -0
- package/.spike/pip-spike-terminal-v2.ts +139 -0
- package/.spike/pip-spike-terminal.ts +169 -0
- package/.spike/pip-spike-xterm-trace.ts +36 -0
- package/.spike/probe-drag-memo-error.mjs +67 -0
- package/.spike/read-dev-db.ts +15 -0
- package/.spike/scratch-editor-target.ts +7 -0
- package/.spike/smoke-unified-chrome-pip.mjs +353 -0
- package/.spike/tunnel.err +63 -42
- package/.spike/vite-restart.err +663 -0
- package/.spike/w.err +569 -94
- package/CHANGELOG.md +25 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/dist/web/assets/adaptive-context-menu-DGCKKeTy.js +1 -0
- package/dist/web/assets/ai-resource-editor-BOHHTTe7.js +1 -0
- package/dist/web/assets/ai-settings-section-BigC_nZR.js +1 -0
- package/dist/web/assets/{angular-html-Ctz0zN01.js → angular-html-BYPaydvh.js} +1 -1
- package/dist/web/assets/{angular-ts-QXHpgB1C.js → angular-ts-Bb4AMKlk.js} +1 -1
- package/dist/web/assets/api-client-DTZEgjBQ.js +1 -0
- package/dist/web/assets/{api-settings-kQHV6pYZ.js → api-settings-Bd3M4YpI.js} +1 -1
- package/dist/web/assets/api-settings-kd1j4iR8.js +1 -0
- package/dist/web/assets/{api-themes-CrK0tkW_.js → api-themes-Cpx419SL.js} +1 -1
- package/dist/web/assets/{apl-b9KVk-O4.js → apl-DB2VjFCS.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-8dCMT9r7.js +1 -0
- package/dist/web/assets/{astro-BlFEeGYr.js → astro-BeoJdVpk.js} +1 -1
- package/dist/web/assets/{audio-preview-CZLmhf__.js → audio-preview-BgFuTn-0.js} +1 -1
- package/dist/web/assets/{blade-CGkj2MUI.js → blade-Cf2q1sPf.js} +1 -1
- package/dist/web/assets/{bsl-CVsbb6ap.js → bsl-W1kOaNpu.js} +1 -1
- package/dist/web/assets/{button-BA_fJOtX.js → button-5zTrkEoN.js} +1 -1
- package/dist/web/assets/{cairo-BbIHl2iE.js → cairo-BoAZFcje.js} +1 -1
- package/dist/web/assets/chat-tab-E3bf_V8X.js +15 -0
- package/dist/web/assets/chevron-down-Dp0rpHb8.js +1 -0
- package/dist/web/assets/clipboard-7fw0Shic.js +1 -0
- package/dist/web/assets/{cobol-BWSGjmWb.js → cobol-B0Nm7RXj.js} +1 -1
- package/dist/web/assets/code-editor-BUlM2gXk.js +10 -0
- package/dist/web/assets/{coffee-CWGNOxds.js → coffee-BakMn3on.js} +1 -1
- package/dist/web/assets/{conflict-editor-CdH_h0vD.js → conflict-editor-BUs5k9q_.js} +4 -4
- package/dist/web/assets/context-menu-Bq92km4s.js +1 -0
- package/dist/web/assets/{cpp-DHGV24J2.js → cpp-BRAJQHK2.js} +1 -1
- package/dist/web/assets/{crystal-Vlwf2UMP.js → crystal-kqqO3zkK.js} +1 -1
- package/dist/web/assets/{csv-preview-CY_454vd.js → csv-preview-ByoDgLAZ.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-D9il5DZS.js → data-grid-overlay-editor-Brkf9z41.js} +1 -1
- package/dist/web/assets/database-viewer-Btc3h8Yu.js +1 -0
- package/dist/web/assets/dialog-DwtGlowE.js +1 -0
- package/dist/web/assets/diff-viewer-CZaDnR4w.js +4 -0
- package/dist/web/assets/docx-preview-DAD_vg59.js +12 -0
- package/dist/web/assets/{edge-Bhqk64zS.js → edge-Cq1ZKZuE.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-gq6ALf2B.js → edit-diff-preview-B_VE7ppQ.js} +1 -1
- package/dist/web/assets/{elixir-D466WCFm.js → elixir-BM_JoJRk.js} +1 -1
- package/dist/web/assets/{elm-Bfs2qNPR.js → elm-Dw7oO08Y.js} +1 -1
- package/dist/web/assets/{erb-WI7jUaB6.js → erb-CfKOGAfk.js} +1 -1
- package/dist/web/assets/{erlang-BCBe8nCc.js → erlang-DlDmGVeE.js} +1 -1
- package/dist/web/assets/{esm-CZs9WVDO.js → esm-DGAP0GFh.js} +1 -1
- package/dist/web/assets/explorer-body-9aBmyVpZ.js +2 -0
- package/dist/web/assets/explorer-window-content-DRtqoSPA.js +1 -0
- package/dist/web/assets/extension-store-Cpfh2vMm.js +1 -0
- package/dist/web/assets/{extension-webview-CuOQrHIS.js → extension-webview-B2OnjuCu.js} +2 -2
- package/dist/web/assets/external-link-B8BynhvY.js +1 -0
- package/dist/web/assets/file-store-aOERIEo-.js +1 -0
- package/dist/web/assets/folder-symlink-B8XrtvQK.js +1 -0
- package/dist/web/assets/format-bytes-WVcFxJuT.js +1 -0
- package/dist/web/assets/{fortran-fixed-form-CXLIv8Ox.js → fortran-fixed-form-XnAhQWxS.js} +1 -1
- package/dist/web/assets/{fsharp-CZsYmiw9.js → fsharp-CdNvy0PG.js} +1 -1
- package/dist/web/assets/{gdresource-BGRvoWfC.js → gdresource-DxuG5i8i.js} +1 -1
- package/dist/web/assets/{git-commit-DH6mY3Km.js → git-commit-3lMonsur.js} +1 -1
- package/dist/web/assets/git-log-panel-Co0PBPHC.js +1 -0
- package/dist/web/assets/{git-rebase-C5Gql6Ma.js → git-rebase-N8Z9kIMf.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-2e_8dfbs.js +1 -0
- package/dist/web/assets/{glide-data-grid-Cehfgrzc.js → glide-data-grid-C4_zIneN.js} +8 -8
- package/dist/web/assets/{glimmer-js-1-6JMJlj.js → glimmer-js-Dp9OXiib.js} +1 -1
- package/dist/web/assets/{glimmer-ts-C_Njj0nK.js → glimmer-ts-CDVKBcDj.js} +1 -1
- package/dist/web/assets/{glsl-DbkxE1TN.js → glsl-DPJxI4us.js} +1 -1
- package/dist/web/assets/{graphql-BK75fiXl.js → graphql-BrFhaQQl.js} +1 -1
- package/dist/web/assets/group-chat-tab-rOd8J8GC.js +1 -0
- package/dist/web/assets/{hack-DG151vit.js → hack-BzXbDMlA.js} +1 -1
- package/dist/web/assets/{haml-DKeIPQWM.js → haml-CPpkX0s5.js} +1 -1
- package/dist/web/assets/{handlebars-398uCI13.js → handlebars-BqrApy52.js} +1 -1
- package/dist/web/assets/{html-NLWdst5q.js → html-DmjUl-OA.js} +1 -1
- package/dist/web/assets/{html-derivative-CJf_Le2z.js → html-derivative-Cnd6XFR5.js} +1 -1
- package/dist/web/assets/{http-G1K2FozA.js → http-DMLWYun2.js} +1 -1
- package/dist/web/assets/{hurl-WDC6sSBz.js → hurl-CKkCVxbj.js} +1 -1
- package/dist/web/assets/{hxml-Dc3jBXuf.js → hxml-CAx19TtQ.js} +1 -1
- package/dist/web/assets/image-preview-DHUoylQ1.js +1 -0
- package/dist/web/assets/index-BImUaigO.js +52 -0
- package/dist/web/assets/index-CSfi6DSU.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-BZi1Gha1.js +1 -0
- package/dist/web/assets/input-DcEk7mA3.js +1 -0
- package/dist/web/assets/{jinja-CWsQj2y0.js → jinja-BmB9YN9l.js} +1 -1
- package/dist/web/assets/{jison-CciJg2NW.js → jison-BaVsKKpw.js} +1 -1
- package/dist/web/assets/{julia-IOQNJAB7.js → julia-BgVBLveX.js} +1 -1
- package/dist/web/assets/{just-BGdHchwS.js → just-C2ENIbl9.js} +1 -1
- package/dist/web/assets/keybindings-store-BGIyqXHI.js +1 -0
- package/dist/web/assets/keybindings-store-CzV2sPls.js +1 -0
- package/dist/web/assets/{latex-D6TOl-L2.js → latex-BiDIWxKL.js} +1 -1
- package/dist/web/assets/{liquid-CVSvu_Wn.js → liquid-NxpapreE.js} +1 -1
- package/dist/web/assets/loader-circle-gSPVxfvH.js +1 -0
- package/dist/web/assets/lock-Did-87UV.js +1 -0
- package/dist/web/assets/{lua-CcKiU6HW.js → lua-DVepQvwH.js} +1 -1
- package/dist/web/assets/markdown-renderer-CcbR33VG.js +1 -0
- package/dist/web/assets/{markdown-renderer-pVzBdkpl.js → markdown-renderer-DlcDhgrF.js} +3 -3
- package/dist/web/assets/{marko--UOicMGo.js → marko-CJ2606Xl.js} +1 -1
- package/dist/web/assets/{mdc-D690aU2C.js → mdc-D2bjqclE.js} +1 -1
- package/dist/web/assets/{media-url-CylYQld1.js → media-url-H17XJF-I.js} +1 -1
- package/dist/web/assets/mobile-bottom-sheet-g-r2hCFa.js +1 -0
- package/dist/web/assets/mobile-explorer-sheet-SIwQXlwA.js +1 -0
- package/dist/web/assets/monitor-Dv8vn_6O.js +1 -0
- package/dist/web/assets/{nextflow-DR8PhpF8.js → nextflow-BtJwUbfF.js} +1 -1
- package/dist/web/assets/{nginx-Qb9pUk1g.js → nginx-j4gyIhHE.js} +1 -1
- package/dist/web/assets/{nim-DLa8R3NS.js → nim-ClH_gjP5.js} +1 -1
- package/dist/web/assets/notification-store-DV5QtUTg.js +1 -0
- package/dist/web/assets/{number-overlay-editor-D9SkFg1Y.js → number-overlay-editor-C1EqJnWq.js} +1 -1
- package/dist/web/assets/overview-panel-vTeeJYVZ.js +1 -0
- package/dist/web/assets/packet-RMMSAZCW-o5htEcin.js +1 -0
- package/dist/web/assets/panel-store-BSnkjgHt.js +1 -0
- package/dist/web/assets/pdf-preview-CqFGRY0Z.js +1 -0
- package/dist/web/assets/pencil-BlIIg6RH.js +1 -0
- package/dist/web/assets/{perl-AMNGIneF.js → perl-C9H7X7CT.js} +1 -1
- package/dist/web/assets/{php-CDsOOxXn.js → php-Bx2U-cQ_.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-BXAIFSAD.js +1 -0
- package/dist/web/assets/play-4MZi1Axp.js +1 -0
- package/dist/web/assets/portal-container-context-Cm5HipFr.js +1 -0
- package/dist/web/assets/{postgres-viewer-CR6VpTrX.js → postgres-viewer-DHwcQucn.js} +3 -3
- package/dist/web/assets/process-table-BGRJNlpn.js +1 -0
- package/dist/web/assets/{project-store-B8BkHXy5.js → project-store--nvQiy75.js} +1 -1
- package/dist/web/assets/{pug-C2kdkhQ6.js → pug-Cw74sKsE.js} +1 -1
- package/dist/web/assets/{qml-DRDkUp75.js → qml-BXD94Eqz.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-Dbt2Aebl.js +1 -0
- package/dist/web/assets/{razor-wY_6wUeC.js → razor-DZ0-Ge0S.js} +1 -1
- package/dist/web/assets/refresh-cw-B06yaBrW.js +1 -0
- package/dist/web/assets/resolve-theme-CRt_7wpd.js +1 -0
- package/dist/web/assets/rotate-ccw-COXu_9oj.js +1 -0
- package/dist/web/assets/{rst-BRpofyNs.js → rst-Dtdh0QUj.js} +1 -1
- package/dist/web/assets/{ruby-ieATBhWm.js → ruby-BC51BW2Q.js} +1 -1
- package/dist/web/assets/{run-in-terminal-CyS5Itj-.js → run-in-terminal-Sd3KyjN4.js} +1 -1
- package/dist/web/assets/{sas-CoOOEbOO.js → sas-BN8i4qOU.js} +1 -1
- package/dist/web/assets/scroll-area-0gljZ6lF.js +1 -0
- package/dist/web/assets/{scss-_nWV0bsb.js → scss-H-QtA7Rz.js} +1 -1
- package/dist/web/assets/send-BlyNJAzy.js +1 -0
- package/dist/web/assets/{send-to-chat-BcO39BDR.js → send-to-chat-u6m8l-rH.js} +1 -1
- package/dist/web/assets/{settings-store-C20lqh0G.js → settings-store-CHY6Kzin.js} +2 -2
- package/dist/web/assets/settings-tab-Dic6TKkU.js +1 -0
- package/dist/web/assets/settings-tab-IY6K1MiZ.js +3 -0
- package/dist/web/assets/{shaderlab-XkqfIfO8.js → shaderlab-CalNsj_x.js} +1 -1
- package/dist/web/assets/shallow-DYo6goJ4.js +1 -0
- package/dist/web/assets/{shellsession-BKBIqTlM.js → shellsession-BFrcoR8x.js} +1 -1
- package/dist/web/assets/{soy-oVDnPzzh.js → soy-CNbku7vP.js} +1 -1
- package/dist/web/assets/{sparql-N2VclEsF.js → sparql-BN_orcVp.js} +1 -1
- package/dist/web/assets/{sql-query-editor-BPYsJDsn.js → sql-query-editor-C6pC3I8l.js} +1 -1
- package/dist/web/assets/sqlite-viewer-DTr5PzUb.js +1 -0
- package/dist/web/assets/{stata-BuuClVDg.js → stata-C6a5Ijzq.js} +1 -1
- package/dist/web/assets/{surrealql-DE5_63MS.js → surrealql-ClplI_HY.js} +1 -1
- package/dist/web/assets/{svelte--YWjlOrK.js → svelte-DNm-UNXB.js} +1 -1
- package/dist/web/assets/switch-DKvKya9y.js +1 -0
- package/dist/web/assets/system-monitor-body-iwGiCns9.js +2 -0
- package/dist/web/assets/system-monitor-tab-DdXQhEel.js +1 -0
- package/dist/web/assets/system-monitor-window-content-BUqRsPK2.js +1 -0
- package/dist/web/assets/tab-host-window-content-BnqGPu8a.js +1 -0
- package/dist/web/assets/{tab-store-DAsvpx4t.js → tab-store-CJuJRapl.js} +1 -1
- package/dist/web/assets/team-member-sheet-LkAFSCvb.js +2 -0
- package/dist/web/assets/team-member-window-content-T4Q5JI-G.js +1 -0
- package/dist/web/assets/{templ-MlpvJx4a.js → templ-CSdqVobu.js} +1 -1
- package/dist/web/assets/terminal-tab-Bb2rZegQ.js +3 -0
- package/dist/web/assets/{tex-0loAoXJG.js → tex-DzSJ4kcZ.js} +1 -1
- package/dist/web/assets/text-wrap-CBT4Gxtg.js +1 -0
- package/dist/web/assets/tool-cards-lhqhrDTT.js +8 -0
- package/dist/web/assets/trash-2-Bk4yYlck.js +1 -0
- package/dist/web/assets/treemap-KZPCXAKY-DUbnl9dz.js +1 -0
- package/dist/web/assets/triangle-alert-B8EYecpe.js +1 -0
- package/dist/web/assets/{ts-tags-pt-Ug0hK.js → ts-tags-C28w3bTg.js} +1 -1
- package/dist/web/assets/{twig-DOePpLOL.js → twig-i5jY3QXB.js} +1 -1
- package/dist/web/assets/upload-BNlX8D07.js +1 -0
- package/dist/web/assets/{use-blob-url-BKyVxOZw.js → use-blob-url-Dsq9OjTh.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-CGucu3fb.js → use-monaco-theme-BZvlsoBv.js} +1 -1
- package/dist/web/assets/use-open-team-member-DkiEjFYE.js +1 -0
- package/dist/web/assets/{use-websocket-C-Rxv8JN.js → use-websocket-B8R50f7f.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-DrwG0NYt.js → vendor-mermaid-By4AVvJY.js} +1 -1
- package/dist/web/assets/video-preview-m0-3p7QZ.js +1 -0
- package/dist/web/assets/{vue-D6f-rprj.js → vue-Ceyet_lv.js} +1 -1
- package/dist/web/assets/{vue-html-DEYRkS1V.js → vue-html-GuU7g_hg.js} +1 -1
- package/dist/web/assets/{vue-vine-CxSG2ojf.js → vue-vine-CxXoaJr2.js} +1 -1
- package/dist/web/assets/window-store-CsEW6jiE.js +1 -0
- package/dist/web/assets/{xml-DhrxbATj.js → xml-CbqOVhkP.js} +1 -1
- package/dist/web/assets/{xsl-CU0ZndUO.js → xsl-KfAmCauy.js} +1 -1
- package/dist/web/index.html +56 -27
- package/dist/web/sw.js +1 -1
- package/docs/code-standards.md +8 -0
- package/docs/codebase-summary.md +19 -5
- package/docs/design-guidelines.md +13 -2
- package/docs/project-roadmap.md +9 -0
- package/docs/system-architecture.md +83 -6
- package/package.json +1 -1
- package/src/web/components/chat/file-picker.tsx +5 -2
- package/src/web/components/chat/mode-selector.tsx +5 -2
- package/src/web/components/chat/model-selector.tsx +5 -2
- package/src/web/components/chat/model-thinking-selector.tsx +5 -2
- package/src/web/components/chat/provider-selector.tsx +5 -2
- package/src/web/components/chat/slash-command-picker.tsx +5 -2
- package/src/web/components/editor/code-editor.tsx +13 -3
- package/src/web/components/editor/conflict-editor.tsx +9 -0
- package/src/web/components/editor/diff-viewer.tsx +9 -0
- package/src/web/components/floating-window/floating-window.tsx +23 -15
- package/src/web/components/floating-window/pip/pip-caption-button.tsx +108 -0
- package/src/web/components/floating-window/pip/pip-focus-target.ts +58 -0
- package/src/web/components/floating-window/pip/pip-geometry.ts +46 -0
- package/src/web/components/floating-window/pip/pip-host.ts +156 -0
- package/src/web/components/floating-window/pip/pip-key-forward.ts +59 -0
- package/src/web/components/floating-window/pip/pip-resize-signal.ts +33 -0
- package/src/web/components/floating-window/pip/pip-style-copy.ts +108 -0
- package/src/web/components/floating-window/pip/pip-support.ts +33 -0
- package/src/web/components/floating-window/tab-host-window-content.tsx +80 -0
- package/src/web/components/floating-window/use-window-body-element.ts +67 -0
- package/src/web/components/floating-window/use-window-drag.ts +5 -0
- package/src/web/components/floating-window/use-window-gesture-context.ts +14 -0
- package/src/web/components/floating-window/use-window-resize.ts +5 -0
- package/src/web/components/floating-window/window-content-registry.ts +4 -16
- package/src/web/components/floating-window/window-layer.tsx +1 -2
- package/src/web/components/floating-window/window-persistence.ts +9 -2
- package/src/web/components/floating-window/window-pip-placeholder.tsx +31 -0
- package/src/web/components/floating-window/window-pip-registry.ts +69 -0
- package/src/web/components/floating-window/window-skin-chrome.tsx +21 -0
- package/src/web/components/floating-window/window-store-types.ts +8 -2
- package/src/web/components/layout/reparenting-tab.tsx +136 -0
- package/src/web/components/layout/tab-bar.tsx +2 -0
- package/src/web/components/layout/tab-pool-collect.ts +38 -2
- package/src/web/components/layout/tab-pool-registry.ts +48 -0
- package/src/web/components/layout/tab-pool.tsx +54 -168
- package/src/web/components/layout/tab-pop-out-menu-item.tsx +42 -0
- package/src/web/components/layout/use-window-panel-reconcile.ts +48 -0
- package/src/web/components/os-explorer/skins/macos-window-chrome.tsx +16 -7
- package/src/web/components/os-explorer/skins/windows-window-chrome.tsx +14 -8
- package/src/web/components/system/build-kill-request.ts +28 -1
- package/src/web/components/system/chart-scale.ts +3 -3
- package/src/web/components/system/kill-confirm-dialog.tsx +44 -28
- package/src/web/components/system/metric-chart-canvas.tsx +1 -2
- package/src/web/components/system/process-columns-grid.ts +39 -29
- package/src/web/components/system/process-group-row.tsx +31 -8
- package/src/web/components/system/process-row.tsx +6 -7
- package/src/web/components/system/process-table-model.ts +4 -22
- package/src/web/components/system/process-table-toolbar.tsx +12 -43
- package/src/web/components/system/process-table.tsx +22 -63
- package/src/web/components/system/sortable-header.tsx +33 -8
- package/src/web/components/system/system-monitor-body.tsx +1 -1
- package/src/web/components/system/use-column-widths.ts +91 -0
- package/src/web/components/system/use-process-kill.ts +100 -0
- package/src/web/components/ui/context-menu.tsx +10 -3
- package/src/web/components/ui/dialog.tsx +10 -1
- package/src/web/components/ui/dropdown-menu.tsx +10 -3
- package/src/web/components/ui/mobile-bottom-sheet.tsx +7 -4
- package/src/web/components/ui/portal-container-context.tsx +40 -0
- package/src/web/components/ui/select.tsx +3 -1
- package/src/web/components/ui/tooltip.tsx +3 -1
- package/src/web/hooks/use-terminal.ts +24 -3
- package/src/web/stores/panel-store.ts +72 -7
- package/src/web/stores/panel-utils.ts +36 -0
- package/src/web/stores/window-panel-actions.ts +211 -0
- package/src/web/stores/window-panel-persistence.ts +115 -0
- package/src/web/stores/window-panel-reconcile.ts +97 -0
- package/src/web/styles/globals.css +5 -3
- package/src/web/theme/builtin/aurora.ts +8 -5
- package/dist/web/assets/ai-resource-editor-DFCuNe-P.js +0 -1
- package/dist/web/assets/api-client-CzABTreM.js +0 -1
- package/dist/web/assets/api-settings-nblja3Rk.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-C7ra20CE.js +0 -1
- package/dist/web/assets/chat-tab-flMPLoKb.js +0 -15
- package/dist/web/assets/code-editor-C2VQbXzw.js +0 -10
- package/dist/web/assets/context-menu-Cq0u3SK9.js +0 -1
- package/dist/web/assets/database-viewer-BCZOTrPT.js +0 -1
- package/dist/web/assets/diff-viewer-BwfMN-nW.js +0 -4
- package/dist/web/assets/docx-preview-D3Sdh_aT.js +0 -12
- package/dist/web/assets/explorer-body-BKOMM566.js +0 -2
- package/dist/web/assets/explorer-window-content-Bbzijk8t.js +0 -1
- package/dist/web/assets/format-bytes-Dk7B73B7.js +0 -1
- package/dist/web/assets/git-log-panel-DcV7PhqW.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-D4hYLNJU.js +0 -1
- package/dist/web/assets/group-chat-tab-BVPYxjz-.js +0 -1
- package/dist/web/assets/image-preview-D-Ok10eZ.js +0 -1
- package/dist/web/assets/index-CerSxGdf.js +0 -52
- package/dist/web/assets/index-DRA_OpWY.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-lRlrTlkb.js +0 -1
- package/dist/web/assets/keybindings-store-aIY40k2o.js +0 -1
- package/dist/web/assets/markdown-renderer-I89kyQhe.js +0 -1
- package/dist/web/assets/mobile-bottom-sheet-BUOJgiaZ.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-5Uj9ZMM9.js +0 -1
- package/dist/web/assets/notification-store-DJ-OtAG3.js +0 -1
- package/dist/web/assets/overview-panel-Cgi3UPY1.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-BIhk1mvp.js +0 -1
- package/dist/web/assets/panel-store-DCD82zc1.js +0 -1
- package/dist/web/assets/pdf-preview-Di5xHFyT.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-SLS6i76l.js +0 -1
- package/dist/web/assets/process-table-DL6shbzt.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-JJ6O1Er_.js +0 -1
- package/dist/web/assets/resolve-theme-IdhSR-gx.js +0 -1
- package/dist/web/assets/settings-tab-jbiQwozX.js +0 -1
- package/dist/web/assets/sqlite-viewer-Bq9ZJA2y.js +0 -1
- package/dist/web/assets/system-monitor-body-CKEYWyN9.js +0 -2
- package/dist/web/assets/system-monitor-tab-WjhFFHyM.js +0 -1
- package/dist/web/assets/system-monitor-window-content-CMiISeoC.js +0 -1
- package/dist/web/assets/team-member-sheet-oY5EkH53.js +0 -2
- package/dist/web/assets/team-member-window-content-yCBeI_5X.js +0 -1
- package/dist/web/assets/terminal-tab-BxHtMR7h.js +0 -3
- package/dist/web/assets/tool-cards-BcGrk1zk.js +0 -8
- package/dist/web/assets/treemap-KZPCXAKY-Be3KNmns.js +0 -1
- package/dist/web/assets/use-open-team-member-DdrRXj-M.js +0 -1
- package/dist/web/assets/video-preview-DYAbbmd0.js +0 -1
- package/src/web/components/floating-window/default-window-chrome.tsx +0 -71
- package/src/web/components/os-explorer/skins/explorer-skin-chrome.tsx +0 -17
- package/src/web/components/system/sparkline-canvas.tsx +0 -63
- /package/dist/web/assets/{abap-MRbmsXOs.js → abap-C6nB04tn.js} +0 -0
- /package/dist/web/assets/{actionscript-3-BDDHKqa4.js → actionscript-3-Vx4rw2sC.js} +0 -0
- /package/dist/web/assets/{ada-DpuV9KwV.js → ada-Ccd8vM20.js} +0 -0
- /package/dist/web/assets/{andromeeda-D6qUCKJo.js → andromeeda-DnBX5xMg.js} +0 -0
- /package/dist/web/assets/{apache-CX1NU_Tl.js → apache-B-9_sx0g.js} +0 -0
- /package/dist/web/assets/{apex-B4-aoQ3K.js → apex-_wPVBYoL.js} +0 -0
- /package/dist/web/assets/{api-client-BSB14J4w.js → api-client-aQb5hHE5.js} +0 -0
- /package/dist/web/assets/{applescript-Dm9NI6KR.js → applescript-CcvD-qr0.js} +0 -0
- /package/dist/web/assets/{apply-theme-BH_d4amk.js → apply-theme-BeLRfsSu.js} +0 -0
- /package/dist/web/assets/{ara-BGN8GAYn.js → ara-C40Ztoms.js} +0 -0
- /package/dist/web/assets/{asciidoc-s_yVuR3t.js → asciidoc-CybYkJV4.js} +0 -0
- /package/dist/web/assets/{asm-BzQdSvTe.js → asm-9eOKwzR0.js} +0 -0
- /package/dist/web/assets/{aurora-x-BJTLETG_.js → aurora-x-B5ZH4t7t.js} +0 -0
- /package/dist/web/assets/{awk-BQEGWxvX.js → awk-Dqilob-2.js} +0 -0
- /package/dist/web/assets/{ayu-dark-CpTORmLc.js → ayu-dark-CPgRhf5r.js} +0 -0
- /package/dist/web/assets/{ayu-light-D8PVj-e6.js → ayu-light-Do06Q52f.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-DyElQHtm.js → ayu-mirage-_BhxcrxY.js} +0 -0
- /package/dist/web/assets/{ballerina-C82NkRtj.js → ballerina-DDwK54QW.js} +0 -0
- /package/dist/web/assets/{bat-0X7DtGI-.js → bat-BGyGlzGk.js} +0 -0
- /package/dist/web/assets/{beancount-Xgvy6YRl.js → beancount-CRWBdapI.js} +0 -0
- /package/dist/web/assets/{berry-CCT2pZcQ.js → berry-DbSFn2gO.js} +0 -0
- /package/dist/web/assets/{bibtex-CpXgZ9M_.js → bibtex-QpsV9gm9.js} +0 -0
- /package/dist/web/assets/{bicep-fkeSA62l.js → bicep-CxS8CUve.js} +0 -0
- /package/dist/web/assets/{bird2-Co7SeAI8.js → bird2-CuZhQzYR.js} +0 -0
- /package/dist/web/assets/{bot-CunPuEdW.js → bot-D6sx11y0.js} +0 -0
- /package/dist/web/assets/{c-BpmrTvJh.js → c-9bnDqE3g.js} +0 -0
- /package/dist/web/assets/{c3-BZ_Z0RrR.js → c3-JMZTrOhA.js} +0 -0
- /package/dist/web/assets/{cadence-B0GBC8OF.js → cadence-DsDNayJS.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-omBzUBBq.js → can-open-in-ppm-BJfpynya.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-DIt_xR7m.js → catppuccin-frappe-DYIKXXBe.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-Dw40AcqA.js → catppuccin-latte-IYYOnUJ8.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-BJ7DfVEF.js → catppuccin-macchiato-BrJ_7mfS.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-NI2lfXzU.js → catppuccin-mocha-gzSu7v7-.js} +0 -0
- /package/dist/web/assets/{chevron-right-BLOtX8Vy.js → chevron-right-De-Wz5Lz.js} +0 -0
- /package/dist/web/assets/{circle-hjUGru6R.js → circle-BCy0AUCo.js} +0 -0
- /package/dist/web/assets/{clarity-F39L0xMJ.js → clarity-DLzsC0Hb.js} +0 -0
- /package/dist/web/assets/{clock-CvhF4DJP.js → clock-C1CQUQPZ.js} +0 -0
- /package/dist/web/assets/{clojure-DloMkd8K.js → clojure-DS1BvoTt.js} +0 -0
- /package/dist/web/assets/{cmake-CwerJun_.js → cmake-CDvsJwYp.js} +0 -0
- /package/dist/web/assets/{code-DDPoqVri.js → code-DJgYrFzm.js} +0 -0
- /package/dist/web/assets/{codeowners-CQyVz4Qf.js → codeowners-BjsYiflJ.js} +0 -0
- /package/dist/web/assets/{codeql-Bb229zuY.js → codeql-CG-7ARat.js} +0 -0
- /package/dist/web/assets/{common-lisp-KMlIwq5I.js → common-lisp-DSO1Lbh9.js} +0 -0
- /package/dist/web/assets/{copy-DKn11Pye.js → copy-BewHj66E.js} +0 -0
- /package/dist/web/assets/{coq-DHX_3IIo.js → coq-DYdh3oP_.js} +0 -0
- /package/dist/web/assets/{csharp-CEmCZv9Q.js → csharp-DxEBz5s0.js} +0 -0
- /package/dist/web/assets/{css-CqZqBD05.js → css-dal3Q-8C.js} +0 -0
- /package/dist/web/assets/{csv-l1g_V5MV.js → csv-CpqjMGiA.js} +0 -0
- /package/dist/web/assets/{csv-parser-CnS0wqr6.js → csv-parser-3umBA4NY.js} +0 -0
- /package/dist/web/assets/{cue-BrxRmilg.js → cue-DinoJ_5k.js} +0 -0
- /package/dist/web/assets/{cypher-rZkKwqZc.js → cypher-DzxqmkUW.js} +0 -0
- /package/dist/web/assets/{d-BALR-QuP.js → d-BxNwJuEC.js} +0 -0
- /package/dist/web/assets/{dark-plus-Cj7rVwh-.js → dark-plus-Clmv3V-0.js} +0 -0
- /package/dist/web/assets/{dart-CWQcdkXk.js → dart-BWVtXD35.js} +0 -0
- /package/dist/web/assets/{data-grid-types-CYvF-xK5.js → data-grid-types-B4n51Fck.js} +0 -0
- /package/dist/web/assets/{database-BH0urvbs.js → database-Y3ESDK4-.js} +0 -0
- /package/dist/web/assets/{dax-BNHD-QBA.js → dax-DsImuWgJ.js} +0 -0
- /package/dist/web/assets/{desktop-CN0IXPrP.js → desktop-B0jJuG2r.js} +0 -0
- /package/dist/web/assets/{diff-BtOXapES.js → diff-CWqAAxoE.js} +0 -0
- /package/dist/web/assets/{dist-DB1Oaqcc.js → dist-B-EXdSYE.js} +0 -0
- /package/dist/web/assets/{dist-Di6G_5jU.js → dist-C0eNNfbt.js} +0 -0
- /package/dist/web/assets/{docker-DNsAo_qO.js → docker-CYFCJdIL.js} +0 -0
- /package/dist/web/assets/{dotenv-D6Pl6aOU.js → dotenv-CKDUjURg.js} +0 -0
- /package/dist/web/assets/{download-zCATy4x4.js → download-DG3_9xXy.js} +0 -0
- /package/dist/web/assets/{dracula-C3m-w12D.js → dracula-CSoPspHE.js} +0 -0
- /package/dist/web/assets/{dracula-soft-BYqvXm6Z.js → dracula-soft-BuFjw5L1.js} +0 -0
- /package/dist/web/assets/{dream-maker-DZAPbPTi.js → dream-maker-B6PdDRZu.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-Ao5SW8ay.js → emacs-lisp-CMcXvD0G.js} +0 -0
- /package/dist/web/assets/{everforest-dark-uC8QYotQ.js → everforest-dark-cU8-wYzi.js} +0 -0
- /package/dist/web/assets/{everforest-light-CTgsVNMr.js → everforest-light-KhorllqK.js} +0 -0
- /package/dist/web/assets/{fennel-WuBw6IR1.js → fennel-BCELZK87.js} +0 -0
- /package/dist/web/assets/{file-exclamation-point-COAwgNGw.js → file-exclamation-point-CZ4DiR75.js} +0 -0
- /package/dist/web/assets/{fish-Byy9dusu.js → fish-BQc0y_jy.js} +0 -0
- /package/dist/web/assets/{fluent-CDxb_sgw.js → fluent-BTINnwF6.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-DKWSTamT.js → fortran-free-form-BQim9wDz.js} +0 -0
- /package/dist/web/assets/{gdscript-prYHMgtD.js → gdscript-03w5u8un.js} +0 -0
- /package/dist/web/assets/{gdshader-D-RM-SQk.js → gdshader-Dp2daNzU.js} +0 -0
- /package/dist/web/assets/{genie-CowkJhej.js → genie-CoNcjPiD.js} +0 -0
- /package/dist/web/assets/{gherkin-CLFpoVYJ.js → gherkin-NrPOSPQL.js} +0 -0
- /package/dist/web/assets/{github-dark-default-BK_1Qgb8.js → github-dark-default-DOKw-1cI.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-Cv9lj6f-.js → github-dark-dimmed-DJRNMkEA.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-CW5ZgIDh.js → github-dark-high-contrast-Bj7jJ_Gs.js} +0 -0
- /package/dist/web/assets/{github-dark-nW2F-RLd.js → github-dark-z4RE9ZqA.js} +0 -0
- /package/dist/web/assets/{github-light-D4PqBHGZ.js → github-light-B4k_yMFu.js} +0 -0
- /package/dist/web/assets/{github-light-default-B-cHtJJV.js → github-light-default-BHc-Uzhs.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-Dt0HJq50.js → github-light-high-contrast-CHisIywL.js} +0 -0
- /package/dist/web/assets/{gleam-DwSyXJWP.js → gleam-CfbypZ4q.js} +0 -0
- /package/dist/web/assets/{gn-Cv5WEzHc.js → gn-YoAjyhJa.js} +0 -0
- /package/dist/web/assets/{gnuplot-D7bRdY_M.js → gnuplot-xVkc1f1c.js} +0 -0
- /package/dist/web/assets/{go-DiRVJ22U.js → go-GPCwP3Gu.js} +0 -0
- /package/dist/web/assets/{groovy-BHAgLTeO.js → groovy-Crh4Yww9.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-C8QZ3gzY.js → gruvbox-dark-hard-BluUFuDS.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Dp1eeid2.js → gruvbox-dark-medium-Cxk1FWQp.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-Cwq0dWVj.js → gruvbox-dark-soft-CHXMiFmj.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-CLQCq2-w.js → gruvbox-light-hard-B6eKPjAD.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-APHsh6Mo.js → gruvbox-light-medium-pDwMm1ut.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-CX-fYc64.js → gruvbox-light-soft-Cxu6kHqK.js} +0 -0
- /package/dist/web/assets/{haskell-C4Tw-TDa.js → haskell-C6JKHjbK.js} +0 -0
- /package/dist/web/assets/{haxe-B7fSduTQ.js → haxe-BuBNdF0a.js} +0 -0
- /package/dist/web/assets/{hcl-Bi3ho6yC.js → hcl-DZpDdGj2.js} +0 -0
- /package/dist/web/assets/{hjson-BXwae2M1.js → hjson-CBkhYRuo.js} +0 -0
- /package/dist/web/assets/{hlsl-CRuQLBSa.js → hlsl-C5nMxnp9.js} +0 -0
- /package/dist/web/assets/{horizon-DtdQ6qjv.js → horizon-VAFvA6R6.js} +0 -0
- /package/dist/web/assets/{horizon-bright-BXSF_uKq.js → horizon-bright-BC0xn7T3.js} +0 -0
- /package/dist/web/assets/{houston-C6ASpUjT.js → houston-BxrUivmz.js} +0 -0
- /package/dist/web/assets/{hy-BrWBHtHY.js → hy-DLtOXoaH.js} +0 -0
- /package/dist/web/assets/{image-gallery-CGvlv8zR.js → image-gallery-Gnf7TdCR.js} +0 -0
- /package/dist/web/assets/{imba-C_JJXckG.js → imba-BdpFXM2W.js} +0 -0
- /package/dist/web/assets/{ini-DEE_vSHQ.js → ini-BInJO_T0.js} +0 -0
- /package/dist/web/assets/{java-CnmpcjcT.js → java-Dxuddnkb.js} +0 -0
- /package/dist/web/assets/{javascript-C-M7sa2e.js → javascript-CVamJfLY.js} +0 -0
- /package/dist/web/assets/{json-BjZ3dAk6.js → json-BLZ0ozYk.js} +0 -0
- /package/dist/web/assets/{json5-BQ2vpqjp.js → json5-BnXzmMKs.js} +0 -0
- /package/dist/web/assets/{jsonc-C5MjoWGx.js → jsonc-CSgkSga7.js} +0 -0
- /package/dist/web/assets/{jsonl-JvhrmQJI.js → jsonl-CY5W2nF2.js} +0 -0
- /package/dist/web/assets/{jsonnet-C5pdZVuo.js → jsonnet-CEJYH3sF.js} +0 -0
- /package/dist/web/assets/{jssm-Db17uybX.js → jssm-CsJc7Uhk.js} +0 -0
- /package/dist/web/assets/{jsx-tJp2Cyqu.js → jsx-jCiRaHU7.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-BculT_Y1.js → kanagawa-dragon-DfoA_GxX.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-yQgHUGwU.js → kanagawa-lotus-BOLYJpWd.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-CyGoWN4d.js → kanagawa-wave-DfKlbT1G.js} +0 -0
- /package/dist/web/assets/{kdl-Bn8R1REF.js → kdl-CmssIFTi.js} +0 -0
- /package/dist/web/assets/{kotlin-DWughK73.js → kotlin-BRsNgp6x.js} +0 -0
- /package/dist/web/assets/{kusto--serPb0x.js → kusto-T8X9jUab.js} +0 -0
- /package/dist/web/assets/{laserwave-DfAPJ1tt.js → laserwave-DkJMVJ8Z.js} +0 -0
- /package/dist/web/assets/{lean-UAm1HOKl.js → lean-C65KGVPq.js} +0 -0
- /package/dist/web/assets/{less-Xj6Ka0fF.js → less-exIPdDlf.js} +0 -0
- /package/dist/web/assets/{lib-DgNcCtj3.js → lib-mC1NZfuw.js} +0 -0
- /package/dist/web/assets/{libesm-CPh1Av0r.js → libesm-DSutUtl1.js} +0 -0
- /package/dist/web/assets/{light-plus-BfS7gPcj.js → light-plus-DiHUXAZt.js} +0 -0
- /package/dist/web/assets/{llvm-DvWiVwvU.js → llvm-CJBnEzFM.js} +0 -0
- /package/dist/web/assets/{log-Bo5z9bNz.js → log-DxMyUMuP.js} +0 -0
- /package/dist/web/assets/{logo-CrIjcMZT.js → logo-CC9NKzca.js} +0 -0
- /package/dist/web/assets/{luau-C9TsYwNt.js → luau-Bw10I6zf.js} +0 -0
- /package/dist/web/assets/{make-GYq4qVbx.js → make-C_6M90m6.js} +0 -0
- /package/dist/web/assets/{markdown-p5mNledS.js → markdown-bR9_R-LF.js} +0 -0
- /package/dist/web/assets/{material-theme-CWYOVxsP.js → material-theme-BUx2Qujn.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-BDGty7YH.js → material-theme-darker-CRJrPgKm.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-JbgOgV3n.js → material-theme-lighter-CUAYppPa.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-EMOpLpa2.js → material-theme-ocean-DuINF1fg.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-CY5hgm7h.js → material-theme-palenight-W6_icGCj.js} +0 -0
- /package/dist/web/assets/{matlab-C-He_NTH.js → matlab-rfqn_b3z.js} +0 -0
- /package/dist/web/assets/{mdx-Cr2TR5fj.js → mdx-Bh1B_YOJ.js} +0 -0
- /package/dist/web/assets/{mermaid-BYc0-B0U.js → mermaid-Dxf6Ya2k.js} +0 -0
- /package/dist/web/assets/{min-dark-CMJwqJqB.js → min-dark-CivZ2L0h.js} +0 -0
- /package/dist/web/assets/{min-light-Cx6vFiEX.js → min-light-B1Ykqtma.js} +0 -0
- /package/dist/web/assets/{mipsasm-Bjer5HwK.js → mipsasm-DynHaM0d.js} +0 -0
- /package/dist/web/assets/{mojo-t90PR-Y2.js → mojo-Clw5PbGH.js} +0 -0
- /package/dist/web/assets/{monokai-dci_PdCm.js → monokai-DFIooFZT.js} +0 -0
- /package/dist/web/assets/{moonbit-AAzI-Hd-.js → moonbit-DnBSZWGM.js} +0 -0
- /package/dist/web/assets/{move-kwBhnhrG.js → move-CCcL2PfL.js} +0 -0
- /package/dist/web/assets/{narrat-CXww2YQL.js → narrat-BDarg5Lk.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-CJXsuXGB.js → nextflow-groovy-DrZOeCNW.js} +0 -0
- /package/dist/web/assets/{night-owl-_GD204Wr.js → night-owl-BnZZCK7v.js} +0 -0
- /package/dist/web/assets/{night-owl-light-DNgkw3kl.js → night-owl-light-CEwBsVHS.js} +0 -0
- /package/dist/web/assets/{nix-BgiorKFd.js → nix-amCzOPMR.js} +0 -0
- /package/dist/web/assets/{nord-CaBmcxFC.js → nord-BIs2BC6q.js} +0 -0
- /package/dist/web/assets/{nushell-DUh4UGx1.js → nushell-Y02fo4yw.js} +0 -0
- /package/dist/web/assets/{objective-c-Cl9KU86S.js → objective-c-7xOZtWW1.js} +0 -0
- /package/dist/web/assets/{objective-cpp-DVkIA8A0.js → objective-cpp-eVtjUvVB.js} +0 -0
- /package/dist/web/assets/{ocaml-vvoHTak0.js → ocaml-Bi6ZwqG4.js} +0 -0
- /package/dist/web/assets/{odin-Cyjw8woZ.js → odin-8xf1Y0S9.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-B1cviILi.js → one-dark-pro-hVv-_y_b.js} +0 -0
- /package/dist/web/assets/{one-light-BPGUJeYI.js → one-light-CJJ3MtHt.js} +0 -0
- /package/dist/web/assets/{openscad-BLMdTUU8.js → openscad-B2HcywLk.js} +0 -0
- /package/dist/web/assets/{pascal-i5niy3e7.js → pascal-C7fllV_7.js} +0 -0
- /package/dist/web/assets/{pkl-CuOV7SsU.js → pkl-qvLC1IWP.js} +0 -0
- /package/dist/web/assets/{plastic-Bf_zE5KL.js → plastic-BT1wHZv2.js} +0 -0
- /package/dist/web/assets/{plsql-cfqEtMD3.js → plsql-1MsmdmJD.js} +0 -0
- /package/dist/web/assets/{plus-BM-Ty0fp.js → plus-BhMZqFsN.js} +0 -0
- /package/dist/web/assets/{po-CFJcWgjr.js → po-BigskytD.js} +0 -0
- /package/dist/web/assets/{poimandres-G5YoaIFf.js → poimandres-DuklniF7.js} +0 -0
- /package/dist/web/assets/{polar-rcjs3i9R.js → polar-e2pjN_sp.js} +0 -0
- /package/dist/web/assets/{postcss-CBKY1FTZ.js → postcss-DtAeYjWw.js} +0 -0
- /package/dist/web/assets/{powerquery-Db7SXLbO.js → powerquery-Cr2PtehI.js} +0 -0
- /package/dist/web/assets/{powershell-LFy3MUTP.js → powershell-BxFTGLJ4.js} +0 -0
- /package/dist/web/assets/{prisma-BE8GR1lH.js → prisma-COWJiwdZ.js} +0 -0
- /package/dist/web/assets/{prolog-DOn5lR1e.js → prolog-DFvGKRx6.js} +0 -0
- /package/dist/web/assets/{proto-wr-TpAK_.js → proto-CcK80lFt.js} +0 -0
- /package/dist/web/assets/{puppet-D2Dkv62u.js → puppet-BF60bZ4F.js} +0 -0
- /package/dist/web/assets/{purescript-DvgJgPdy.js → purescript-zL_69MzU.js} +0 -0
- /package/dist/web/assets/{python-C7TlkHhK.js → python-CIwKhfb_.js} +0 -0
- /package/dist/web/assets/{qmldir-CtZMwhcW.js → qmldir-CXNe04Lo.js} +0 -0
- /package/dist/web/assets/{qss-dNZ2btnl.js → qss-C8lAWe6G.js} +0 -0
- /package/dist/web/assets/{r-BlqnNkHS.js → r-CL5ZVL3_.js} +0 -0
- /package/dist/web/assets/{racket-CzRQrGc4.js → racket-BBfh0uh0.js} +0 -0
- /package/dist/web/assets/{raku-BwutWhBF.js → raku-o6E8R5Q3.js} +0 -0
- /package/dist/web/assets/{react-I_EwPIZi.js → react-CBTnT4sY.js} +0 -0
- /package/dist/web/assets/{red-BMXMywmL.js → red-GomRF2Xp.js} +0 -0
- /package/dist/web/assets/{reg-BqOKrIOw.js → reg-_ffDIvx9.js} +0 -0
- /package/dist/web/assets/{regexp-Bero1BN2.js → regexp-Bl5sF65W.js} +0 -0
- /package/dist/web/assets/{rel-BXKWIElA.js → rel-DgMdqDuj.js} +0 -0
- /package/dist/web/assets/{riscv-BbbBYJBo.js → riscv-8b3-Ye4X.js} +0 -0
- /package/dist/web/assets/{ron-BtVTWW4b.js → ron-DrmU1KHP.js} +0 -0
- /package/dist/web/assets/{rose-pine-hvkvQzvU.js → rose-pine-C_-E2tF1.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-D7lwhUei.js → rose-pine-dawn-DEfhNlFH.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-CoHDmsIw.js → rose-pine-moon-BdvX3gdn.js} +0 -0
- /package/dist/web/assets/{rosmsg-CdaZFgI8.js → rosmsg-B6GhGpd5.js} +0 -0
- /package/dist/web/assets/{rust-Dy2qsphD.js → rust-DmC97M1-.js} +0 -0
- /package/dist/web/assets/{sass-Bz7NVxDb.js → sass-B0tJOHLM.js} +0 -0
- /package/dist/web/assets/{save-C4z1p51e.js → save-CtIGOjmx.js} +0 -0
- /package/dist/web/assets/{scala-sXSwsNyq.js → scala-n8zaFOXv.js} +0 -0
- /package/dist/web/assets/{scheme-DS7TUbN7.js → scheme-BKD9ztBU.js} +0 -0
- /package/dist/web/assets/{sdbl-iISFvIep.js → sdbl-D8gB5-rS.js} +0 -0
- /package/dist/web/assets/{shellscript-BkIl4ApC.js → shellscript-BfXlf_Ao.js} +0 -0
- /package/dist/web/assets/{shield-check-C5laObJv.js → shield-check-B0iGUiOK.js} +0 -0
- /package/dist/web/assets/{shield-off-DULFtyLS.js → shield-off-BNLWBhcq.js} +0 -0
- /package/dist/web/assets/{slack-dark-B1eg0_m2.js → slack-dark-CylEddse.js} +0 -0
- /package/dist/web/assets/{slack-ochin-mdkc1ldu.js → slack-ochin-w8wg8vuC.js} +0 -0
- /package/dist/web/assets/{smalltalk-CORtd03t.js → smalltalk-hiAXA64u.js} +0 -0
- /package/dist/web/assets/{snazzy-light-Bc6Q2G89.js → snazzy-light-BwlV3Xty.js} +0 -0
- /package/dist/web/assets/{solarized-dark-CF83zRXM.js → solarized-dark-vmNGdykz.js} +0 -0
- /package/dist/web/assets/{solarized-light-BoKQZaYU.js → solarized-light-DGv_0JXq.js} +0 -0
- /package/dist/web/assets/{solidity-DOgi1N4w.js → solidity-Y7QSH0B1.js} +0 -0
- /package/dist/web/assets/{sparkles-Dqsf3hq6.js → sparkles-Dh-_J2Rd.js} +0 -0
- /package/dist/web/assets/{splunk-BXfw5ZLy.js → splunk-BtgpLjHi.js} +0 -0
- /package/dist/web/assets/{sql-BcVs-eX4.js → sql-CRHYhOia.js} +0 -0
- /package/dist/web/assets/{ssh-config-CLCtNkkd.js → ssh-config-DOqSs3au.js} +0 -0
- /package/dist/web/assets/{stylus-DdP992aO.js → stylus-crCAEaBT.js} +0 -0
- /package/dist/web/assets/{swift-BnGb6Lgc.js → swift-eE3rT7TC.js} +0 -0
- /package/dist/web/assets/{synthwave-84-cg9264l9.js → synthwave-84-BWy14LBX.js} +0 -0
- /package/dist/web/assets/{system-verilog-GbHpszs3.js → system-verilog-DC0-DZM4.js} +0 -0
- /package/dist/web/assets/{systemd-Dz-bNsDQ.js → systemd-to5p6N8I.js} +0 -0
- /package/dist/web/assets/{table-CHmxuh6M.js → table-Bbc783ko.js} +0 -0
- /package/dist/web/assets/{talonscript-CFIidoBA.js → talonscript-DoTcU8wm.js} +0 -0
- /package/dist/web/assets/{tasl-Cu-TRTKx.js → tasl-cNZvUs95.js} +0 -0
- /package/dist/web/assets/{tcl-DwCHCB1K.js → tcl-mZE8sZH8.js} +0 -0
- /package/dist/web/assets/{terraform-BzZw4T1z.js → terraform-CqHPpIfu.js} +0 -0
- /package/dist/web/assets/{tokyo-night-RKsj2-tv.js → tokyo-night-Tp2rTIZd.js} +0 -0
- /package/dist/web/assets/{toml-CirbOgDP.js → toml-CU_vscgn.js} +0 -0
- /package/dist/web/assets/{tsv-CraRcWdU.js → tsv-ik7iFB9D.js} +0 -0
- /package/dist/web/assets/{tsx-CjFg0lgw.js → tsx-BMvvMKiv.js} +0 -0
- /package/dist/web/assets/{turtle-B-WBIOa6.js → turtle-DBqKCq9o.js} +0 -0
- /package/dist/web/assets/{typescript-MpLarYn1.js → typescript-DXJDmMZZ.js} +0 -0
- /package/dist/web/assets/{typespec-B7QlHMZ_.js → typespec-CBseCMtZ.js} +0 -0
- /package/dist/web/assets/{typst-CcHSKFZj.js → typst-4oY25U9y.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-BXGVvczu.js → use-is-mobile-D1weanbj.js} +0 -0
- /package/dist/web/assets/{utils-DoY4yTP-.js → utils-BNRH507E.js} +0 -0
- /package/dist/web/assets/{v-xTGhWZuD.js → v-BXaZ_1oy.js} +0 -0
- /package/dist/web/assets/{vala-DwebjH3H.js → vala-BImWJE7z.js} +0 -0
- /package/dist/web/assets/{vb-BxJT2RAy.js → vb-CLfj2_sy.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-CU6zIYMB.js → vendor-xterm-_yyu5n32.js} +0 -0
- /package/dist/web/assets/{verilog-CvV6RHPD.js → verilog-BWa8Ed2b.js} +0 -0
- /package/dist/web/assets/{vesper-CY3qd7E_.js → vesper-BGj1cg6m.js} +0 -0
- /package/dist/web/assets/{vhdl-B9ZX-sUL.js → vhdl-mByK7mlx.js} +0 -0
- /package/dist/web/assets/{viml-Y42h_ExQ.js → viml-CugmlW5g.js} +0 -0
- /package/dist/web/assets/{vitesse-black-DkXP6Sv-.js → vitesse-black-vPdpy9TL.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-B9CkIB6r.js → vitesse-dark-BuQK3dUh.js} +0 -0
- /package/dist/web/assets/{vitesse-light-C7DHrJCK.js → vitesse-light-B3nz1XL9.js} +0 -0
- /package/dist/web/assets/{vyper-Cnd9s5j-.js → vyper-DH_GTIHG.js} +0 -0
- /package/dist/web/assets/{wasm-DQE71XTB.js → wasm-C-LF9GjX.js} +0 -0
- /package/dist/web/assets/{wasm-iWe7Fzjc.js → wasm-DfVbVJlW.js} +0 -0
- /package/dist/web/assets/{wenyan-Bc5Pnm9l.js → wenyan-CzQkHyHx.js} +0 -0
- /package/dist/web/assets/{wgsl-D-YUAWkP.js → wgsl-Du_M1DW9.js} +0 -0
- /package/dist/web/assets/{wikitext-DYQDUYf2.js → wikitext-BWN_w5S7.js} +0 -0
- /package/dist/web/assets/{wit-P6u7mfwy.js → wit-BR13v5HD.js} +0 -0
- /package/dist/web/assets/{wolfram-6SXLUqao.js → wolfram-BoXxp_Xl.js} +0 -0
- /package/dist/web/assets/{x-BcqfTY_7.js → x-nhriOX9o.js} +0 -0
- /package/dist/web/assets/{yaml-CQiMLVcZ.js → yaml-DpR_EF-d.js} +0 -0
- /package/dist/web/assets/{zenscript-DFMywo_P.js → zenscript-C-EdUtUd.js} +0 -0
- /package/dist/web/assets/{zig-CtAjaIiF.js → zig-BD4eA5nU.js} +0 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { clampColumnWidth, columnWidthPx, type ColumnWidths, type ResizableColumnKey } from "./process-columns-grid";
|
|
3
|
+
|
|
4
|
+
/** One entry for the whole app: column widths are a per-device preference, not
|
|
5
|
+
* per-window state, so they survive reloads and apply to the mobile tab too. */
|
|
6
|
+
export const COLUMN_WIDTHS_STORAGE_KEY = "ppm-sysmon-col-widths";
|
|
7
|
+
|
|
8
|
+
function loadWidths(): ColumnWidths {
|
|
9
|
+
try {
|
|
10
|
+
const raw = localStorage.getItem(COLUMN_WIDTHS_STORAGE_KEY);
|
|
11
|
+
if (!raw) return {};
|
|
12
|
+
const parsed = JSON.parse(raw) as Record<string, unknown>;
|
|
13
|
+
const out: ColumnWidths = {};
|
|
14
|
+
for (const k of ["cpu", "ram", "disk", "gpu", "net"] as ResizableColumnKey[]) {
|
|
15
|
+
const v = parsed[k];
|
|
16
|
+
if (typeof v === "number" && Number.isFinite(v)) out[k] = clampColumnWidth(v);
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
} catch {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function saveWidths(widths: ColumnWidths): void {
|
|
25
|
+
try {
|
|
26
|
+
if (Object.keys(widths).length === 0) localStorage.removeItem(COLUMN_WIDTHS_STORAGE_KEY);
|
|
27
|
+
else localStorage.setItem(COLUMN_WIDTHS_STORAGE_KEY, JSON.stringify(widths));
|
|
28
|
+
} catch {
|
|
29
|
+
/* private mode / quota — widths just don't persist */
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Drag-to-resize state for the process table's fixed-width columns.
|
|
35
|
+
*
|
|
36
|
+
* `begin(key, event)` is wired to `pointerdown` on a header's right-edge handle;
|
|
37
|
+
* the drag then follows `pointermove`/`pointerup` on `window`, so the pointer may
|
|
38
|
+
* leave the handle (or the window body) without dropping the drag. Widths persist
|
|
39
|
+
* on release, not on every move. `reset(key)` (double-click) returns one column
|
|
40
|
+
* to its default.
|
|
41
|
+
*/
|
|
42
|
+
export function useColumnWidths() {
|
|
43
|
+
const [widths, setWidths] = useState<ColumnWidths>(() => (typeof localStorage === "undefined" ? {} : loadWidths()));
|
|
44
|
+
const drag = useRef<{ key: ResizableColumnKey; startX: number; startWidth: number } | null>(null);
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
const onMove = (e: PointerEvent) => {
|
|
48
|
+
const d = drag.current;
|
|
49
|
+
if (!d) return;
|
|
50
|
+
const next = clampColumnWidth(d.startWidth + (e.clientX - d.startX));
|
|
51
|
+
setWidths((prev) => (prev[d.key] === next ? prev : { ...prev, [d.key]: next }));
|
|
52
|
+
};
|
|
53
|
+
const onUp = () => {
|
|
54
|
+
if (!drag.current) return;
|
|
55
|
+
drag.current = null;
|
|
56
|
+
document.body.style.cursor = "";
|
|
57
|
+
setWidths((prev) => {
|
|
58
|
+
saveWidths(prev);
|
|
59
|
+
return prev;
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
window.addEventListener("pointermove", onMove);
|
|
63
|
+
window.addEventListener("pointerup", onUp);
|
|
64
|
+
window.addEventListener("pointercancel", onUp);
|
|
65
|
+
return () => {
|
|
66
|
+
window.removeEventListener("pointermove", onMove);
|
|
67
|
+
window.removeEventListener("pointerup", onUp);
|
|
68
|
+
window.removeEventListener("pointercancel", onUp);
|
|
69
|
+
};
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
const begin = useCallback(
|
|
73
|
+
(key: ResizableColumnKey, e: { clientX: number; preventDefault: () => void }) => {
|
|
74
|
+
e.preventDefault(); // no text selection while dragging
|
|
75
|
+
drag.current = { key, startX: e.clientX, startWidth: columnWidthPx(key, widths) };
|
|
76
|
+
document.body.style.cursor = "col-resize";
|
|
77
|
+
},
|
|
78
|
+
[widths],
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const reset = useCallback((key: ResizableColumnKey) => {
|
|
82
|
+
setWidths((prev) => {
|
|
83
|
+
if (!(key in prev)) return prev;
|
|
84
|
+
const { [key]: _dropped, ...rest } = prev;
|
|
85
|
+
saveWidths(rest);
|
|
86
|
+
return rest;
|
|
87
|
+
});
|
|
88
|
+
}, []);
|
|
89
|
+
|
|
90
|
+
return { widths, begin, reset };
|
|
91
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { useCallback, useMemo, useState } from "react";
|
|
2
|
+
import { toast } from "sonner";
|
|
3
|
+
import { getAuthToken } from "@/lib/api-client";
|
|
4
|
+
import { buildGroupKillRequests, buildKillRequest, isGroupProtected, type KillTarget } from "./build-kill-request";
|
|
5
|
+
import type {
|
|
6
|
+
KillProcessRequest,
|
|
7
|
+
KillProcessResult,
|
|
8
|
+
MetricsSnapshot,
|
|
9
|
+
ProcessGroup,
|
|
10
|
+
ProcessInfo,
|
|
11
|
+
} from "../../../types/system-metrics";
|
|
12
|
+
|
|
13
|
+
/** `api.post` (src/web/lib/api-client.ts) has no way to add a bespoke header, and the
|
|
14
|
+
* kill route requires `X-PPM-Request: 1` — CSRF hardening, since a cross-origin HTML
|
|
15
|
+
* form cannot set a custom header, so a preflight becomes mandatory. A raw fetch here
|
|
16
|
+
* mirrors `handleResponse`'s single-Error-on-any-failure contract exactly, so the
|
|
17
|
+
* caller still has exactly one catch path. */
|
|
18
|
+
export async function killProcess(request: KillProcessRequest): Promise<KillProcessResult> {
|
|
19
|
+
const token = getAuthToken();
|
|
20
|
+
const headers: Record<string, string> = {
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
"X-PPM-Request": "1",
|
|
23
|
+
};
|
|
24
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
25
|
+
|
|
26
|
+
const res = await fetch("/api/system/resources/kill", {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers,
|
|
29
|
+
body: JSON.stringify(request),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
let json: { ok: boolean; data?: KillProcessResult; error?: string };
|
|
33
|
+
try {
|
|
34
|
+
json = await res.json();
|
|
35
|
+
} catch {
|
|
36
|
+
throw new Error(res.ok ? "Empty response from server" : `Server error (HTTP ${res.status})`);
|
|
37
|
+
}
|
|
38
|
+
if (json.ok === false) throw new Error(json.error ?? `HTTP ${res.status}`);
|
|
39
|
+
return json.data as KillProcessResult;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Pending-kill state plus the confirm handler for single processes and whole app
|
|
44
|
+
* groups. Members are resolved against the CURRENT snapshot at click time, so a
|
|
45
|
+
* helper that exited since the row was drawn is simply not in the list.
|
|
46
|
+
*/
|
|
47
|
+
export function useProcessKill(snapshot: MetricsSnapshot) {
|
|
48
|
+
const [pendingKill, setPendingKill] = useState<KillTarget | null>(null);
|
|
49
|
+
|
|
50
|
+
const membersOf = useCallback(
|
|
51
|
+
(group: ProcessGroup): ProcessInfo[] => {
|
|
52
|
+
const byPid = new Map(snapshot.processes.map((p) => [p.pid, p]));
|
|
53
|
+
return group.pids.map((pid) => byPid.get(pid)).filter((p): p is ProcessInfo => !!p);
|
|
54
|
+
},
|
|
55
|
+
[snapshot.processes],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
/** group key → true when any member is refused by the guard (button disabled). */
|
|
59
|
+
const groupProtected = useMemo(
|
|
60
|
+
() => new Map(snapshot.groups.map((g) => [g.key, isGroupProtected(membersOf(g))])),
|
|
61
|
+
[snapshot.groups, membersOf],
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const requestKillProcess = useCallback((proc: ProcessInfo) => setPendingKill({ kind: "process", proc }), []);
|
|
65
|
+
const requestKillGroup = useCallback(
|
|
66
|
+
(group: ProcessGroup) => setPendingKill({ kind: "group", group, members: membersOf(group) }),
|
|
67
|
+
[membersOf],
|
|
68
|
+
);
|
|
69
|
+
const cancelKill = useCallback(() => setPendingKill(null), []);
|
|
70
|
+
|
|
71
|
+
const confirmKill = useCallback(async (target: KillTarget, tree: boolean) => {
|
|
72
|
+
setPendingKill(null);
|
|
73
|
+
if (target.kind === "process") {
|
|
74
|
+
const { proc } = target;
|
|
75
|
+
try {
|
|
76
|
+
await killProcess(buildKillRequest(proc, tree));
|
|
77
|
+
toast.success(`Ended ${proc.name} (${proc.pid})`);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
toast.error(e instanceof Error ? e.message : `Failed to end ${proc.name}`);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Whole app: one tree kill on the root when it exists, else one per member.
|
|
84
|
+
// Requests run sequentially so the guard's 403/409 for one member is reported,
|
|
85
|
+
// not lost in a Promise.all rejection.
|
|
86
|
+
const { group, members } = target;
|
|
87
|
+
const failures: string[] = [];
|
|
88
|
+
for (const req of buildGroupKillRequests(group, members)) {
|
|
89
|
+
try {
|
|
90
|
+
await killProcess(req);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
failures.push(e instanceof Error ? e.message : `pid ${req.pid}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (failures.length === 0) toast.success(`Ended ${group.label} (${members.length} processes)`);
|
|
96
|
+
else toast.error(`${group.label}: ${failures.length} of ${members.length} could not be ended — ${failures[0]}`);
|
|
97
|
+
}, []);
|
|
98
|
+
|
|
99
|
+
return { pendingKill, groupProtected, requestKillProcess, requestKillGroup, confirmKill, cancelKill };
|
|
100
|
+
}
|
|
@@ -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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
|
|
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
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
+
}
|
|
@@ -3,6 +3,7 @@ import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
|
|
3
3
|
import { Select as SelectPrimitive } from "radix-ui"
|
|
4
4
|
|
|
5
5
|
import { cn } from "@/lib/utils"
|
|
6
|
+
import { usePortalContainer } from "./portal-container-context"
|
|
6
7
|
|
|
7
8
|
function Select({
|
|
8
9
|
...props
|
|
@@ -55,8 +56,9 @@ function SelectContent({
|
|
|
55
56
|
align = "center",
|
|
56
57
|
...props
|
|
57
58
|
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
|
59
|
+
const container = usePortalContainer()
|
|
58
60
|
return (
|
|
59
|
-
<SelectPrimitive.Portal>
|
|
61
|
+
<SelectPrimitive.Portal container={container}>
|
|
60
62
|
<SelectPrimitive.Content
|
|
61
63
|
data-slot="select-content"
|
|
62
64
|
className={cn(
|
|
@@ -4,6 +4,7 @@ import * as React from "react"
|
|
|
4
4
|
import { Tooltip as TooltipPrimitive } from "radix-ui"
|
|
5
5
|
|
|
6
6
|
import { cn } from "@/lib/utils"
|
|
7
|
+
import { usePortalContainer } from "./portal-container-context"
|
|
7
8
|
|
|
8
9
|
function TooltipProvider({
|
|
9
10
|
delayDuration = 0,
|
|
@@ -36,8 +37,9 @@ function TooltipContent({
|
|
|
36
37
|
children,
|
|
37
38
|
...props
|
|
38
39
|
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
40
|
+
const container = usePortalContainer()
|
|
39
41
|
return (
|
|
40
|
-
<TooltipPrimitive.Portal>
|
|
42
|
+
<TooltipPrimitive.Portal container={container}>
|
|
41
43
|
<TooltipPrimitive.Content
|
|
42
44
|
data-slot="tooltip-content"
|
|
43
45
|
sideOffset={sideOffset}
|
|
@@ -8,6 +8,7 @@ import { useSettingsStore } from "@/stores/settings-store";
|
|
|
8
8
|
import { buildXtermTheme } from "@/theme/adapters/xterm-adapter";
|
|
9
9
|
import { resolveTheme as resolvePpmTheme } from "@/theme/resolve-theme";
|
|
10
10
|
import { getCurrentAppliedTheme, THEME_CHANGE_EVENT } from "@/theme/apply-theme";
|
|
11
|
+
import { onHostResize } from "@/components/floating-window/pip/pip-resize-signal";
|
|
11
12
|
import type { PpmTheme } from "@/theme/types";
|
|
12
13
|
|
|
13
14
|
/** Current active PpmTheme → xterm ITheme (prefers the live applied theme). */
|
|
@@ -400,10 +401,17 @@ export function useTerminal(
|
|
|
400
401
|
|
|
401
402
|
// Keepalive heartbeat + zombie detection (covers suspend/sleep/network drop)
|
|
402
403
|
const heartbeatInterval = setInterval(checkConnection, HEARTBEAT_INTERVAL_MS);
|
|
404
|
+
// Always the main document: a tab mounts there and is only moved into a
|
|
405
|
+
// picture-in-picture window afterwards, and this is resolved once at mount.
|
|
406
|
+
// So a terminal that is visible in PiP while the main tab is hidden is still
|
|
407
|
+
// treated as hidden and does not reconnect until the main tab comes back —
|
|
408
|
+
// checkConnection() reads the main document's visibility directly too, so
|
|
409
|
+
// re-resolving this one would not change that on its own.
|
|
410
|
+
const hostDoc = container.ownerDocument;
|
|
403
411
|
const onVisibility = () => {
|
|
404
|
-
if (
|
|
412
|
+
if (hostDoc.visibilityState === "visible") checkConnection();
|
|
405
413
|
};
|
|
406
|
-
|
|
414
|
+
hostDoc.addEventListener("visibilitychange", onVisibility);
|
|
407
415
|
|
|
408
416
|
// ResizeObserver for auto-fit — skip when tab is hidden (0 dimensions).
|
|
409
417
|
// Debounced: an on-screen keyboard resizes the container many times as it
|
|
@@ -426,6 +434,18 @@ export function useTerminal(
|
|
|
426
434
|
});
|
|
427
435
|
resizeObserver.observe(container);
|
|
428
436
|
|
|
437
|
+
// Host-driven resize (picture-in-picture): the ResizeObserver above only
|
|
438
|
+
// reports a PiP-driven size seconds later, so refit as soon as the host says
|
|
439
|
+
// its size changed.
|
|
440
|
+
const unsubHostResize = onHostResize(container, () => {
|
|
441
|
+
try {
|
|
442
|
+
fitAddon.fit();
|
|
443
|
+
sendResize();
|
|
444
|
+
} catch {
|
|
445
|
+
// Ignore fit errors while the tab is detached or tearing down
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
|
|
429
449
|
// React to theme changes — the theme-change event fires after CSS vars are
|
|
430
450
|
// applied, covering style, mode, system-OS, and imported-theme swaps.
|
|
431
451
|
const onThemeChange = () => { term.options.theme = currentXtermTheme(); };
|
|
@@ -434,10 +454,11 @@ export function useTerminal(
|
|
|
434
454
|
|
|
435
455
|
return () => {
|
|
436
456
|
unsubTheme();
|
|
457
|
+
unsubHostResize();
|
|
437
458
|
if (fitTimer) clearTimeout(fitTimer);
|
|
438
459
|
resizeObserver.disconnect();
|
|
439
460
|
clearInterval(heartbeatInterval);
|
|
440
|
-
|
|
461
|
+
hostDoc.removeEventListener("visibilitychange", onVisibility);
|
|
441
462
|
if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
|
|
442
463
|
if (readyQuietTimer.current) clearTimeout(readyQuietTimer.current);
|
|
443
464
|
if (readyMaxTimer.current) clearTimeout(readyMaxTimer.current);
|
|
@@ -17,7 +17,17 @@ import {
|
|
|
17
17
|
deriveTabId,
|
|
18
18
|
visibleTabs,
|
|
19
19
|
DOCK_PANEL_ID,
|
|
20
|
+
isWindowPanelId,
|
|
20
21
|
} from "./panel-utils";
|
|
22
|
+
import {
|
|
23
|
+
makePopOutTab,
|
|
24
|
+
makeRedockFromWindow,
|
|
25
|
+
hydrateWindowPanels,
|
|
26
|
+
stripProjectFromWindowPanels,
|
|
27
|
+
syncWindowPanel,
|
|
28
|
+
} from "./window-panel-actions";
|
|
29
|
+
import { saveWindowPanels } from "./window-panel-persistence";
|
|
30
|
+
import { useWindowStore } from "@/components/floating-window/window-store";
|
|
21
31
|
import {
|
|
22
32
|
makeToggleDock,
|
|
23
33
|
makeSetDockVisible,
|
|
@@ -120,6 +130,16 @@ export interface PanelStore {
|
|
|
120
130
|
/** Park a terminal from a grid panel into __dock__ without killing the PTY session. */
|
|
121
131
|
redockTab: (tabId: string, fromPanelId: string) => void;
|
|
122
132
|
|
|
133
|
+
// Floating-window actions
|
|
134
|
+
/**
|
|
135
|
+
* Detach a tab into a floating window, returning the window id — or null when the
|
|
136
|
+
* request is rejected (mobile, a tab type that cannot detach, or the window cap),
|
|
137
|
+
* in which case nothing changed and the caller reports it.
|
|
138
|
+
*/
|
|
139
|
+
popOutTab: (tabId: string, fromPanelId: string) => string | null;
|
|
140
|
+
/** Send a window's tabs back to the grid and drop its panel. Runs on window close. */
|
|
141
|
+
redockFromWindow: (windowId: string, originPanelId?: string | null) => void;
|
|
142
|
+
|
|
123
143
|
// Helpers
|
|
124
144
|
getPanelForTab: (tabId: string) => Panel | undefined;
|
|
125
145
|
isMobile: () => boolean;
|
|
@@ -171,6 +191,22 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
171
191
|
return panelId ?? get().focusedPanelId;
|
|
172
192
|
}
|
|
173
193
|
|
|
194
|
+
/**
|
|
195
|
+
* The focus to commit when a tab was activated in `pid`.
|
|
196
|
+
*
|
|
197
|
+
* Focus decides where the next `openTab()` with no explicit panel lands, and a
|
|
198
|
+
* floating window renders no tab bar — a tab opened inside one would be unreachable
|
|
199
|
+
* until the window closes, and the id would be persisted with the layout. So
|
|
200
|
+
* activating a tab that lives in a window leaves focus where it was: the window is
|
|
201
|
+
* already in front of the user, nothing more is needed to "focus" it.
|
|
202
|
+
*
|
|
203
|
+
* Every caller here picks `pid` by scanning the whole `panels` map, which includes
|
|
204
|
+
* the off-grid window panels.
|
|
205
|
+
*/
|
|
206
|
+
function focusAfterActivate(pid: string): string {
|
|
207
|
+
return isWindowPanelId(pid) ? get().focusedPanelId : pid;
|
|
208
|
+
}
|
|
209
|
+
|
|
174
210
|
return {
|
|
175
211
|
...defaultLayout(),
|
|
176
212
|
currentProject: null,
|
|
@@ -188,8 +224,13 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
188
224
|
toggleDockExpanded: () => set((s) => ({ dockExpanded: !s.dockExpanded })),
|
|
189
225
|
openInDock: makeOpenInDock(set, get),
|
|
190
226
|
redockTab: makeRedockTab(set, get),
|
|
227
|
+
popOutTab: makePopOutTab(set, get),
|
|
228
|
+
redockFromWindow: makeRedockFromWindow(set, get),
|
|
191
229
|
|
|
192
230
|
switchProject: (projectName) => {
|
|
231
|
+
// Window panels are global, not part of any project's layout blob, so they are
|
|
232
|
+
// merged into the flat map once — before the first project reads it.
|
|
233
|
+
hydrateWindowPanels(set, get);
|
|
193
234
|
const { currentProject, panels, grid, focusedPanelId, projectGrids, projectFocused, dock, projectDock } = get();
|
|
194
235
|
|
|
195
236
|
// No-op if same project
|
|
@@ -401,18 +442,26 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
401
442
|
};
|
|
402
443
|
}
|
|
403
444
|
|
|
445
|
+
// A window panel is off-grid, so the loop above never reaches it: a floating
|
|
446
|
+
// window would keep showing a tab of a project that no longer exists.
|
|
447
|
+
const stripped = stripProjectFromWindowPanels(cleanedPanels, projectName);
|
|
448
|
+
|
|
404
449
|
set({
|
|
405
450
|
projectGrids: newGrids,
|
|
406
451
|
projectFocused: newFocused,
|
|
407
452
|
projectDock: newProjectDock,
|
|
408
|
-
panels:
|
|
453
|
+
panels: stripped.panels,
|
|
409
454
|
currentProject: null,
|
|
410
455
|
});
|
|
456
|
+
saveWindowPanels(stripped.panels);
|
|
457
|
+
for (const windowId of stripped.windowIdsToClose) useWindowStore.getState().close(windowId);
|
|
411
458
|
// Re-trigger full load from localStorage
|
|
412
459
|
get().switchProject(projectName);
|
|
413
460
|
},
|
|
414
461
|
|
|
415
462
|
setFocusedPanel: (panelId) => {
|
|
463
|
+
// Focus drives where the next tab opens, so it must stay on a real, on-grid panel.
|
|
464
|
+
if (isWindowPanelId(panelId)) return;
|
|
416
465
|
if (get().panels[panelId]) set({ focusedPanelId: panelId });
|
|
417
466
|
},
|
|
418
467
|
|
|
@@ -451,7 +500,7 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
451
500
|
const existing = p.tabs.find((t) => t.type === "chat" && t.metadata?.sessionId === sid);
|
|
452
501
|
if (existing) {
|
|
453
502
|
set((s) => ({
|
|
454
|
-
focusedPanelId: p.id,
|
|
503
|
+
focusedPanelId: focusAfterActivate(p.id),
|
|
455
504
|
panels: {
|
|
456
505
|
...s.panels,
|
|
457
506
|
[p.id]: { ...p, tabs: stampActive(p.tabs, existing.id), activeTabId: existing.id, tabHistory: pushHistory(p.tabHistory, existing.id) },
|
|
@@ -469,7 +518,7 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
469
518
|
const existing = p.tabs.find((t) => t.id === baseId);
|
|
470
519
|
if (existing) {
|
|
471
520
|
set((s) => ({
|
|
472
|
-
focusedPanelId: p.id,
|
|
521
|
+
focusedPanelId: focusAfterActivate(p.id),
|
|
473
522
|
panels: {
|
|
474
523
|
...s.panels,
|
|
475
524
|
[p.id]: {
|
|
@@ -581,8 +630,12 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
581
630
|
// no-op for it) so the map-delete would destroy the reserved dock panel, and a
|
|
582
631
|
// later redock (moveTab → __dock__) would silently no-op (to === undefined),
|
|
583
632
|
// leaving the terminal stuck in the grid while an empty dock opens.
|
|
633
|
+
// A window panel is exempt from the "keep the last panel" rule: that rule
|
|
634
|
+
// protects the grid from ending up with nothing to render, and a window panel
|
|
635
|
+
// is not in the grid. Leaving it behind would strand an empty floating window.
|
|
584
636
|
const gridPanelCount = s.grid.flat().length;
|
|
585
|
-
|
|
637
|
+
const removable = isWindowPanelId(pid) || (gridPanelCount > 1 && pid !== DOCK_PANEL_ID);
|
|
638
|
+
if (newTabs.length === 0 && removable) {
|
|
586
639
|
const { [pid]: _, ...rest } = s.panels;
|
|
587
640
|
const newGrid = gridRemovePanel(s.grid, pid);
|
|
588
641
|
// Focus must land on a panel still in the grid — Object.keys(rest)
|
|
@@ -603,6 +656,9 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
603
656
|
if (pid === DOCK_PANEL_ID && activeProjectDockTabCount(get) === 0) {
|
|
604
657
|
get().setDockVisible(false);
|
|
605
658
|
}
|
|
659
|
+
|
|
660
|
+
// A window that lost its last tab closes with it — it can never show anything again.
|
|
661
|
+
if (isWindowPanelId(pid)) syncWindowPanel(set, get, pid);
|
|
606
662
|
},
|
|
607
663
|
|
|
608
664
|
setActiveTab: (tabId, panelId?) => {
|
|
@@ -612,7 +668,7 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
612
668
|
set((s) => {
|
|
613
669
|
const p = s.panels[pid]!;
|
|
614
670
|
return {
|
|
615
|
-
focusedPanelId: pid,
|
|
671
|
+
focusedPanelId: focusAfterActivate(pid),
|
|
616
672
|
panels: { ...s.panels, [pid]: { ...p, tabs: stampActive(p.tabs, tabId), activeTabId: tabId, tabHistory: pushHistory(p.tabHistory, tabId) } },
|
|
617
673
|
};
|
|
618
674
|
});
|
|
@@ -667,8 +723,13 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
667
723
|
// Auto-close empty source panel if not last in current grid.
|
|
668
724
|
// Guard: never attempt to remove __dock__ from the grid — it is intentionally
|
|
669
725
|
// absent from grid and gridRemovePanel would be a no-op, but the panels-map
|
|
670
|
-
// delete would destroy the dock panel entirely.
|
|
671
|
-
|
|
726
|
+
// delete would destroy the dock panel entirely. The dock is reserved and outlives
|
|
727
|
+
// its tabs; only its visibility follows them.
|
|
728
|
+
// A window panel is skipped for a different reason: its lifetime belongs to the
|
|
729
|
+
// floating window, so syncWindowPanel below drops it together with the window
|
|
730
|
+
// rather than leaving an open, empty one behind.
|
|
731
|
+
const offGridSource = fromPanelId === DOCK_PANEL_ID || isWindowPanelId(fromPanelId);
|
|
732
|
+
if (fromTabs.length === 0 && gridPanelCount > 1 && !offGridSource) {
|
|
672
733
|
const { [fromPanelId]: _, ...rest } = s.panels;
|
|
673
734
|
return {
|
|
674
735
|
panels: {
|
|
@@ -690,6 +751,10 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
|
|
|
690
751
|
};
|
|
691
752
|
});
|
|
692
753
|
persist();
|
|
754
|
+
|
|
755
|
+
// Moving the last tab out of a window empties it exactly as closing that tab would,
|
|
756
|
+
// and the window has no tab bar to fill itself from — same owner, same outcome.
|
|
757
|
+
if (isWindowPanelId(fromPanelId)) syncWindowPanel(set, get, fromPanelId);
|
|
693
758
|
},
|
|
694
759
|
|
|
695
760
|
splitPanel: (direction, tabId, sourcePanelId, targetPanelId?) => {
|