@hienlh/ppm 0.18.12 → 0.18.13
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/AGENTS.md +17 -5
- package/CHANGELOG.md +22 -0
- package/CLAUDE.md +3 -0
- package/README.md +6 -6
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +19 -1
- package/dist/web/assets/{adaptive-context-menu-DGCKKeTy.js → adaptive-context-menu-CbgPYQmj.js} +1 -1
- package/dist/web/assets/{ai-resource-editor-BOHHTTe7.js → ai-resource-editor-unM2HNmg.js} +1 -1
- package/dist/web/assets/{ai-settings-section-BigC_nZR.js → ai-settings-section-CdktdrwF.js} +1 -1
- package/dist/web/assets/{angular-html-BYPaydvh.js → angular-html-DcpbdcNG.js} +1 -1
- package/dist/web/assets/{angular-ts-Bb4AMKlk.js → angular-ts-Bv_WIk34.js} +1 -1
- package/dist/web/assets/api-client-BogqsFna.js +1 -0
- package/dist/web/assets/{api-settings-Bd3M4YpI.js → api-settings-BdCtex9i.js} +1 -1
- package/dist/web/assets/api-settings-DfXViPKz.js +1 -0
- package/dist/web/assets/{api-themes-Cpx419SL.js → api-themes-pRRoLvgD.js} +1 -1
- package/dist/web/assets/{apl-DB2VjFCS.js → apl-D5_lLszu.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-BzFm9ELu.js +1 -0
- package/dist/web/assets/{astro-BeoJdVpk.js → astro-CBHwddYL.js} +1 -1
- package/dist/web/assets/{audio-preview-BgFuTn-0.js → audio-preview-BD1iDovf.js} +1 -1
- package/dist/web/assets/{blade-Cf2q1sPf.js → blade-DGe1d2Pq.js} +1 -1
- package/dist/web/assets/{bsl-W1kOaNpu.js → bsl-2lyAzRJA.js} +1 -1
- package/dist/web/assets/{button-5zTrkEoN.js → button-2WCAS8dI.js} +1 -1
- package/dist/web/assets/{cairo-BoAZFcje.js → cairo-DjzZeJrq.js} +1 -1
- package/dist/web/assets/chat-tab-DzK8LGti.js +15 -0
- package/dist/web/assets/{cobol-B0Nm7RXj.js → cobol-cC3XIuAJ.js} +1 -1
- package/dist/web/assets/code-editor-CuMfPN9X.js +10 -0
- package/dist/web/assets/{coffee-BakMn3on.js → coffee-4eGboVoU.js} +1 -1
- package/dist/web/assets/{conflict-editor-BUs5k9q_.js → conflict-editor-DdJag4QK.js} +1 -1
- package/dist/web/assets/{context-menu-Bq92km4s.js → context-menu-BO1c9jKy.js} +1 -1
- package/dist/web/assets/{cpp-BRAJQHK2.js → cpp-DF9_G-OU.js} +1 -1
- package/dist/web/assets/{crystal-kqqO3zkK.js → crystal-OavZlzcm.js} +1 -1
- package/dist/web/assets/{csv-preview-ByoDgLAZ.js → csv-preview-DnXwouF3.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-Brkf9z41.js → data-grid-overlay-editor-B9Gpjm3l.js} +1 -1
- package/dist/web/assets/{database-viewer-Btc3h8Yu.js → database-viewer-Dk8oGyZx.js} +1 -1
- package/dist/web/assets/{dialog-DwtGlowE.js → dialog-B-l2ZxVm.js} +1 -1
- package/dist/web/assets/{diff-viewer-CZaDnR4w.js → diff-viewer-eaUBjLCH.js} +1 -1
- package/dist/web/assets/{docx-preview-DAD_vg59.js → docx-preview-DAdHPlhZ.js} +1 -1
- package/dist/web/assets/{edge-Cq1ZKZuE.js → edge-TzYh8OlN.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-B_VE7ppQ.js → edit-diff-preview-DlwP3UIU.js} +1 -1
- package/dist/web/assets/{elixir-BM_JoJRk.js → elixir-Cy3RhKKH.js} +1 -1
- package/dist/web/assets/{elm-Dw7oO08Y.js → elm-BTBZdweK.js} +1 -1
- package/dist/web/assets/{erb-CfKOGAfk.js → erb-dv1mI0y4.js} +1 -1
- package/dist/web/assets/{erlang-DlDmGVeE.js → erlang-DjeiDrkl.js} +1 -1
- package/dist/web/assets/{esm-DGAP0GFh.js → esm-FRwvcesu.js} +1 -1
- package/dist/web/assets/explorer-body-DkCkSVrN.js +2 -0
- package/dist/web/assets/explorer-window-content-MsnTVngu.js +1 -0
- package/dist/web/assets/{extension-store-Cpfh2vMm.js → extension-store-DUaZINyC.js} +1 -1
- package/dist/web/assets/{extension-webview-B2OnjuCu.js → extension-webview-W0qRfaAU.js} +1 -1
- package/dist/web/assets/{file-store-aOERIEo-.js → file-store-yReVbR8q.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-XnAhQWxS.js → fortran-fixed-form-BCH9DAIJ.js} +1 -1
- package/dist/web/assets/{fsharp-CdNvy0PG.js → fsharp-C93NYKYD.js} +1 -1
- package/dist/web/assets/{gdresource-DxuG5i8i.js → gdresource-BcIEE2eX.js} +1 -1
- package/dist/web/assets/{git-commit-3lMonsur.js → git-commit-BVjlXEy8.js} +1 -1
- package/dist/web/assets/{git-log-panel-Co0PBPHC.js → git-log-panel-2_4HuP47.js} +1 -1
- package/dist/web/assets/{git-rebase-N8Z9kIMf.js → git-rebase-Bj_21B1V.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-B72vcb2r.js +1 -0
- package/dist/web/assets/glide-data-grid-B0IY6n_Q.js +138 -0
- package/dist/web/assets/{glimmer-js-Dp9OXiib.js → glimmer-js-C20yj4Zv.js} +1 -1
- package/dist/web/assets/{glimmer-ts-CDVKBcDj.js → glimmer-ts-ipbYC7kh.js} +1 -1
- package/dist/web/assets/{glsl-DPJxI4us.js → glsl-Dovazu3t.js} +1 -1
- package/dist/web/assets/{graphql-BrFhaQQl.js → graphql-DpMlM7hC.js} +1 -1
- package/dist/web/assets/{group-chat-tab-rOd8J8GC.js → group-chat-tab-DRLDHOp6.js} +1 -1
- package/dist/web/assets/{hack-BzXbDMlA.js → hack-gc7LCBuV.js} +1 -1
- package/dist/web/assets/{haml-CPpkX0s5.js → haml-vtlFm9gm.js} +1 -1
- package/dist/web/assets/{handlebars-BqrApy52.js → handlebars-DGKhpCji.js} +1 -1
- package/dist/web/assets/{html-DmjUl-OA.js → html-DvfhQbXb.js} +1 -1
- package/dist/web/assets/{html-derivative-Cnd6XFR5.js → html-derivative-C3oTSSCN.js} +1 -1
- package/dist/web/assets/{http-DMLWYun2.js → http-SG6zLTo7.js} +1 -1
- package/dist/web/assets/{hurl-CKkCVxbj.js → hurl-BWXsYlqW.js} +1 -1
- package/dist/web/assets/{hxml-CAx19TtQ.js → hxml-DUdMzaoh.js} +1 -1
- package/dist/web/assets/{image-preview-DHUoylQ1.js → image-preview-DxgFOV1T.js} +1 -1
- package/dist/web/assets/index-CBn4SOoT.js +52 -0
- package/dist/web/assets/index-DWpNR3nB.css +2 -0
- package/dist/web/assets/info-3K5VOQVL-3irPxRK_.js +1 -0
- package/dist/web/assets/{input-DcEk7mA3.js → input-DuAF-Stj.js} +1 -1
- package/dist/web/assets/{jinja-BmB9YN9l.js → jinja-955w9RKM.js} +1 -1
- package/dist/web/assets/{jison-BaVsKKpw.js → jison-WWfqPo50.js} +1 -1
- package/dist/web/assets/{julia-BgVBLveX.js → julia-BFrfnaNl.js} +1 -1
- package/dist/web/assets/{just-C2ENIbl9.js → just-BrZdyR_I.js} +1 -1
- package/dist/web/assets/{keybindings-store-BGIyqXHI.js → keybindings-store-0Wqfa7Qp.js} +1 -1
- package/dist/web/assets/keybindings-store-d54pv2NK.js +1 -0
- package/dist/web/assets/{latex-BiDIWxKL.js → latex-CwtPPASD.js} +1 -1
- package/dist/web/assets/{liquid-NxpapreE.js → liquid-CYZuvDDd.js} +1 -1
- package/dist/web/assets/{lua-DVepQvwH.js → lua-BV095ZyY.js} +1 -1
- package/dist/web/assets/markdown-renderer-BTS8ifdu.js +1 -0
- package/dist/web/assets/{markdown-renderer-DlcDhgrF.js → markdown-renderer-CnRcKK3d.js} +3 -3
- package/dist/web/assets/{marko-CJ2606Xl.js → marko-CNeljZ1C.js} +1 -1
- package/dist/web/assets/{mdc-D2bjqclE.js → mdc-JfGapShy.js} +1 -1
- package/dist/web/assets/{media-url-H17XJF-I.js → media-url-Bm4PsduE.js} +1 -1
- package/dist/web/assets/{mobile-bottom-sheet-g-r2hCFa.js → mobile-bottom-sheet-M5iZDX7y.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BqK2-WuN.js +1 -0
- package/dist/web/assets/{nextflow-BtJwUbfF.js → nextflow-DD5C8nqX.js} +1 -1
- package/dist/web/assets/{nginx-j4gyIhHE.js → nginx-Ca7B__x_.js} +1 -1
- package/dist/web/assets/{nim-ClH_gjP5.js → nim-B8FogsDw.js} +1 -1
- package/dist/web/assets/notification-store-Yvy1I8gd.js +1 -0
- package/dist/web/assets/{number-overlay-editor-C1EqJnWq.js → number-overlay-editor-DG9xJwEG.js} +1 -1
- package/dist/web/assets/{overview-panel-vTeeJYVZ.js → overview-panel-s53bnl0q.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-Z5jWjFCV.js +1 -0
- package/dist/web/assets/{panel-store-BSnkjgHt.js → panel-store-B4rijFKN.js} +1 -1
- package/dist/web/assets/{pdf-preview-CqFGRY0Z.js → pdf-preview-DYt3aBzM.js} +1 -1
- package/dist/web/assets/{perl-C9H7X7CT.js → perl-BR78hS8R.js} +1 -1
- package/dist/web/assets/{php-Bx2U-cQ_.js → php-DXBOMC9z.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-Bq7UMi8P.js +1 -0
- package/dist/web/assets/{postgres-viewer-DHwcQucn.js → postgres-viewer-BvoWSuoZ.js} +3 -3
- package/dist/web/assets/{process-table-BGRJNlpn.js → process-table-BYwKigiD.js} +1 -1
- package/dist/web/assets/{project-store--nvQiy75.js → project-store-BuY7rxn_.js} +1 -1
- package/dist/web/assets/{pug-Cw74sKsE.js → pug-CvTwVVKV.js} +1 -1
- package/dist/web/assets/{qml-BXD94Eqz.js → qml-CPSoZ1-B.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-BLo4BOTD.js +1 -0
- package/dist/web/assets/{razor-DZ0-Ge0S.js → razor-BUA1YKtF.js} +1 -1
- package/dist/web/assets/{rst-Dtdh0QUj.js → rst-BJsq7ISz.js} +1 -1
- package/dist/web/assets/{ruby-BC51BW2Q.js → ruby-DSDMeYf5.js} +1 -1
- package/dist/web/assets/{run-in-terminal-Sd3KyjN4.js → run-in-terminal-C9xpskFn.js} +1 -1
- package/dist/web/assets/{sas-BN8i4qOU.js → sas-B1nJOjbN.js} +1 -1
- package/dist/web/assets/{scroll-area-0gljZ6lF.js → scroll-area-DBcG1fQo.js} +1 -1
- package/dist/web/assets/{scss-H-QtA7Rz.js → scss-BZFyRiQn.js} +1 -1
- package/dist/web/assets/{send-to-chat-u6m8l-rH.js → send-to-chat-BIg70MUZ.js} +1 -1
- package/dist/web/assets/{settings-store-CHY6Kzin.js → settings-store-GV5pkkke.js} +2 -2
- package/dist/web/assets/{settings-tab-IY6K1MiZ.js → settings-tab-BePvIcjd.js} +1 -1
- package/dist/web/assets/settings-tab-Br-lJSPa.js +1 -0
- package/dist/web/assets/{shaderlab-CalNsj_x.js → shaderlab-laINRXBo.js} +1 -1
- package/dist/web/assets/{shellsession-BFrcoR8x.js → shellsession-BpOOvWVR.js} +1 -1
- package/dist/web/assets/{soy-CNbku7vP.js → soy-BW4d91Sa.js} +1 -1
- package/dist/web/assets/{sparql-BN_orcVp.js → sparql-BsvvyoHn.js} +1 -1
- package/dist/web/assets/{sql-query-editor-C6pC3I8l.js → sql-query-editor-qQce5QJ6.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-DTr5PzUb.js → sqlite-viewer-yhc0YNvu.js} +1 -1
- package/dist/web/assets/{stata-C6a5Ijzq.js → stata-K-dZInUt.js} +1 -1
- package/dist/web/assets/{surrealql-ClplI_HY.js → surrealql-CpNp2sgf.js} +1 -1
- package/dist/web/assets/{svelte-DNm-UNXB.js → svelte-BG6iBA8R.js} +1 -1
- package/dist/web/assets/{switch-DKvKya9y.js → switch-_NcrktLe.js} +1 -1
- package/dist/web/assets/system-monitor-body-D8Z5QXBB.js +2 -0
- package/dist/web/assets/system-monitor-tab-BclYXFTe.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-BUqRsPK2.js → system-monitor-window-content-CaOhD0AU.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-BnqGPu8a.js → tab-host-window-content-f_Rc5rJK.js} +1 -1
- package/dist/web/assets/{tab-store-CJuJRapl.js → tab-store-tFnyPmm4.js} +1 -1
- package/dist/web/assets/team-member-sheet-QDHoYCs0.js +2 -0
- package/dist/web/assets/{team-member-window-content-T4Q5JI-G.js → team-member-window-content-B1jkKkDi.js} +1 -1
- package/dist/web/assets/{templ-CSdqVobu.js → templ-gxNKXmt3.js} +1 -1
- package/dist/web/assets/{terminal-tab-Bb2rZegQ.js → terminal-tab-UvwM0sty.js} +3 -3
- package/dist/web/assets/{tex-DzSJ4kcZ.js → tex-C0ON27g5.js} +1 -1
- package/dist/web/assets/{tool-cards-lhqhrDTT.js → tool-cards-jB7GT-wj.js} +4 -4
- package/dist/web/assets/treemap-KZPCXAKY-dyrE6SW2.js +1 -0
- package/dist/web/assets/{ts-tags-C28w3bTg.js → ts-tags-BX_CTVgQ.js} +1 -1
- package/dist/web/assets/{twig-i5jY3QXB.js → twig-CwL7XnU0.js} +1 -1
- package/dist/web/assets/{use-blob-url-Dsq9OjTh.js → use-blob-url-DcRcjNTE.js} +1 -1
- package/dist/web/assets/use-coarse-long-press-BmI_GVW5.js +1 -0
- package/dist/web/assets/{use-monaco-theme-BZvlsoBv.js → use-monaco-theme--9tOOkDA.js} +1 -1
- package/dist/web/assets/{use-open-team-member-DkiEjFYE.js → use-open-team-member-1hRsHLKg.js} +1 -1
- package/dist/web/assets/{use-websocket-B8R50f7f.js → use-websocket-rxJLbFqy.js} +1 -1
- package/dist/web/assets/{vendor-mermaid-By4AVvJY.js → vendor-mermaid-CHTa6D69.js} +1 -1
- package/dist/web/assets/video-preview-DMhnf2Gi.js +1 -0
- package/dist/web/assets/{vue-Ceyet_lv.js → vue-Bykm6wSS.js} +1 -1
- package/dist/web/assets/{vue-html-GuU7g_hg.js → vue-html-CETdK_iV.js} +1 -1
- package/dist/web/assets/{vue-vine-CxXoaJr2.js → vue-vine-DiJb6fO5.js} +1 -1
- package/dist/web/assets/{window-store-CsEW6jiE.js → window-store-CQ1lghvv.js} +1 -1
- package/dist/web/assets/{xml-CbqOVhkP.js → xml-BNw8J5YW.js} +1 -1
- package/dist/web/assets/{xsl-KfAmCauy.js → xsl-CsG9L31b.js} +1 -1
- package/dist/web/index.html +46 -45
- package/dist/web/sw.js +1 -1
- package/docs/architecture/ai-chat-and-providers.md +309 -0
- package/docs/architecture/data-and-storage.md +356 -0
- package/docs/architecture/extensions.md +496 -0
- package/docs/architecture/integrations.md +214 -0
- package/docs/architecture/workspace-and-ui.md +391 -0
- package/docs/claude-agent-sdk-reference.md +1 -1
- package/docs/code-patterns.md +361 -0
- package/docs/code-standards.md +8 -358
- package/docs/codebase-subsystems.md +364 -0
- package/docs/codebase-summary.md +34 -379
- package/docs/deployment-guide.md +280 -490
- package/docs/design-guidelines.md +1 -1
- package/docs/lessons-learned.md +27 -10
- package/docs/project-changelog.md +18 -806
- package/docs/project-overview-pdr.md +117 -111
- package/docs/project-roadmap.md +89 -133
- package/docs/system-architecture.md +77 -1826
- package/package.json +2 -2
- package/src/cli/commands/autostart.ts +2 -2
- package/src/cli/commands/config-cmd.ts +12 -3
- package/src/cli/commands/status.ts +3 -2
- package/src/cli/commands/stop.ts +32 -4
- package/src/server/index.ts +17 -3
- package/src/server/routes/chat-unread.ts +36 -0
- package/src/server/routes/named-tunnel.ts +132 -0
- package/src/server/routes/tunnel.ts +13 -0
- package/src/server/ws/chat.ts +5 -2
- package/src/services/autostart-register.ts +47 -29
- package/src/services/cloud.service.ts +26 -1
- package/src/services/config-secret-keys.ts +48 -0
- package/src/services/config.service.ts +1 -1
- package/src/services/db.service.ts +62 -12
- package/src/services/extension-rpc-handlers.ts +9 -1
- package/src/services/file-watcher/watch-tree.ts +67 -33
- package/src/services/file-watcher.service.ts +28 -10
- package/src/services/fs-credential-path-guard.ts +103 -0
- package/src/services/fs-path-guard.service.ts +14 -66
- package/src/services/named-tunnel/cloudflare-dns-api.ts +40 -0
- package/src/services/named-tunnel/cloudflare-zone-api.ts +28 -0
- package/src/services/named-tunnel/cloudflared-cert.ts +95 -0
- package/src/services/named-tunnel/cloudflared-exec.ts +50 -0
- package/src/services/named-tunnel/cloudflared-login-helpers.ts +98 -0
- package/src/services/named-tunnel/cloudflared-login.service.ts +182 -0
- package/src/services/named-tunnel/hostname-rules.ts +58 -0
- package/src/services/named-tunnel/login-output-parser.ts +36 -0
- package/src/services/named-tunnel/named-tunnel-args.ts +110 -0
- package/src/services/named-tunnel/named-tunnel-config.ts +67 -0
- package/src/services/named-tunnel/named-tunnel-probe-state.ts +95 -0
- package/src/services/named-tunnel/named-tunnel-readiness.ts +59 -0
- package/src/services/named-tunnel/named-tunnel-runtime.ts +42 -0
- package/src/services/named-tunnel/named-tunnel-setup-confirm.ts +98 -0
- package/src/services/named-tunnel/named-tunnel-setup.service.ts +191 -0
- package/src/services/supervisor-state.ts +67 -2
- package/src/services/supervisor.ts +454 -157
- package/src/services/tunnel-registry-parse.ts +27 -0
- package/src/services/tunnel-registry.service.ts +2 -15
- package/src/services/tunnel.service.ts +12 -3
- package/src/types/config.ts +31 -0
- package/src/web/app.tsx +5 -0
- package/src/web/components/chat/chat-history-bar.tsx +95 -98
- package/src/web/components/chat/chat-toolbar-overflow-menu.tsx +57 -0
- package/src/web/components/chat/session-debug-dialog.tsx +35 -27
- package/src/web/components/layout/cloud-share-named-tunnel-row.tsx +111 -0
- package/src/web/components/layout/cloud-share-popover.tsx +56 -14
- package/src/web/components/shared/connection-lost-overlay.tsx +44 -5
- package/src/web/components/tunnels/named-tunnel/hostname-validation.ts +14 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +96 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-hostname-field.tsx +51 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-login-step.tsx +105 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +159 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-content.tsx +197 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-setup-popup.tsx +57 -0
- package/src/web/components/tunnels/named-tunnel/named-tunnel-step-reducer.ts +191 -0
- package/src/web/components/tunnels/named-tunnel/use-named-tunnel-setup.ts +212 -0
- package/src/web/components/tunnels/tunnel-manager-tab.tsx +4 -0
- package/src/web/hooks/use-global-events.ts +3 -1
- package/src/web/lib/api-named-tunnel.ts +54 -0
- package/src/web/lib/last-known-local-url.ts +23 -0
- package/src/web/stores/notification-store.ts +20 -1
- package/dist/web/assets/api-client-DTZEgjBQ.js +0 -1
- package/dist/web/assets/api-settings-kd1j4iR8.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-8dCMT9r7.js +0 -1
- package/dist/web/assets/chat-tab-E3bf_V8X.js +0 -15
- package/dist/web/assets/code-editor-BUlM2gXk.js +0 -10
- package/dist/web/assets/explorer-body-9aBmyVpZ.js +0 -2
- package/dist/web/assets/explorer-window-content-DRtqoSPA.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-2e_8dfbs.js +0 -1
- package/dist/web/assets/glide-data-grid-C4_zIneN.js +0 -138
- package/dist/web/assets/index-BImUaigO.js +0 -52
- package/dist/web/assets/index-CSfi6DSU.css +0 -2
- package/dist/web/assets/info-3K5VOQVL-BZi1Gha1.js +0 -1
- package/dist/web/assets/keybindings-store-CzV2sPls.js +0 -1
- package/dist/web/assets/markdown-renderer-CcbR33VG.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-SIwQXlwA.js +0 -1
- package/dist/web/assets/notification-store-DV5QtUTg.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-o5htEcin.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-BXAIFSAD.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-Dbt2Aebl.js +0 -1
- package/dist/web/assets/settings-tab-Dic6TKkU.js +0 -1
- package/dist/web/assets/system-monitor-body-iwGiCns9.js +0 -2
- package/dist/web/assets/system-monitor-tab-DdXQhEel.js +0 -1
- package/dist/web/assets/team-member-sheet-LkAFSCvb.js +0 -2
- package/dist/web/assets/treemap-KZPCXAKY-DUbnl9dz.js +0 -1
- package/dist/web/assets/video-preview-m0-3p7QZ.js +0 -1
- /package/dist/web/assets/{abap-C6nB04tn.js → abap-DGlrX_8G.js} +0 -0
- /package/dist/web/assets/{actionscript-3-Vx4rw2sC.js → actionscript-3-CUqRtUJE.js} +0 -0
- /package/dist/web/assets/{ada-Ccd8vM20.js → ada-CY3Spa33.js} +0 -0
- /package/dist/web/assets/{andromeeda-DnBX5xMg.js → andromeeda-Lxs2TJn7.js} +0 -0
- /package/dist/web/assets/{apache-B-9_sx0g.js → apache-DfxgGdgM.js} +0 -0
- /package/dist/web/assets/{apex-_wPVBYoL.js → apex-QVDF1dnI.js} +0 -0
- /package/dist/web/assets/{api-client-aQb5hHE5.js → api-client-oY1tC3tM.js} +0 -0
- /package/dist/web/assets/{applescript-CcvD-qr0.js → applescript-pwJ8QXh-.js} +0 -0
- /package/dist/web/assets/{apply-theme-BeLRfsSu.js → apply-theme-B_bMVL6a.js} +0 -0
- /package/dist/web/assets/{ara-C40Ztoms.js → ara-mPjeX5YX.js} +0 -0
- /package/dist/web/assets/{asciidoc-CybYkJV4.js → asciidoc-Bf7LsDkI.js} +0 -0
- /package/dist/web/assets/{asm-9eOKwzR0.js → asm-rRKC1OUx.js} +0 -0
- /package/dist/web/assets/{aurora-x-B5ZH4t7t.js → aurora-x-DwQr0DkG.js} +0 -0
- /package/dist/web/assets/{awk-Dqilob-2.js → awk-Bpt768fN.js} +0 -0
- /package/dist/web/assets/{ayu-dark-CPgRhf5r.js → ayu-dark-D9lwQHMs.js} +0 -0
- /package/dist/web/assets/{ayu-light-Do06Q52f.js → ayu-light-B4-ptrU5.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-_BhxcrxY.js → ayu-mirage-DAyRzH5Q.js} +0 -0
- /package/dist/web/assets/{ballerina-DDwK54QW.js → ballerina-Cgdpj1HC.js} +0 -0
- /package/dist/web/assets/{bat-BGyGlzGk.js → bat-4FrN36S2.js} +0 -0
- /package/dist/web/assets/{beancount-CRWBdapI.js → beancount-DkcxQER3.js} +0 -0
- /package/dist/web/assets/{berry-DbSFn2gO.js → berry-Dnuut-Ml.js} +0 -0
- /package/dist/web/assets/{bibtex-QpsV9gm9.js → bibtex-CCDwmbuA.js} +0 -0
- /package/dist/web/assets/{bicep-CxS8CUve.js → bicep-BC0ryoGM.js} +0 -0
- /package/dist/web/assets/{bird2-CuZhQzYR.js → bird2-C50V_A-0.js} +0 -0
- /package/dist/web/assets/{c-9bnDqE3g.js → c-Ddorhq0U.js} +0 -0
- /package/dist/web/assets/{c3-JMZTrOhA.js → c3-DdIf5r9E.js} +0 -0
- /package/dist/web/assets/{cadence-DsDNayJS.js → cadence-s9pd1Wng.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-BJfpynya.js → can-open-in-ppm-C6hZV1OP.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-DYIKXXBe.js → catppuccin-frappe-CzJNTyu-.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-IYYOnUJ8.js → catppuccin-latte-BNpt-m7s.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-BrJ_7mfS.js → catppuccin-macchiato-FYGkfsJ5.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-gzSu7v7-.js → catppuccin-mocha-it2g5D1q.js} +0 -0
- /package/dist/web/assets/{clarity-DLzsC0Hb.js → clarity-BVFBTjMe.js} +0 -0
- /package/dist/web/assets/{clipboard-7fw0Shic.js → clipboard-BWznymz7.js} +0 -0
- /package/dist/web/assets/{clojure-DS1BvoTt.js → clojure-BSYvXMI8.js} +0 -0
- /package/dist/web/assets/{cmake-CDvsJwYp.js → cmake-DGEqH-Np.js} +0 -0
- /package/dist/web/assets/{codeowners-BjsYiflJ.js → codeowners-D8FCOxWg.js} +0 -0
- /package/dist/web/assets/{codeql-CG-7ARat.js → codeql-COV0eluG.js} +0 -0
- /package/dist/web/assets/{common-lisp-DSO1Lbh9.js → common-lisp-xHnZUlgt.js} +0 -0
- /package/dist/web/assets/{coq-DYdh3oP_.js → coq-Cs_2i3Xg.js} +0 -0
- /package/dist/web/assets/{csharp-DxEBz5s0.js → csharp-B1ONB4PT.js} +0 -0
- /package/dist/web/assets/{css-dal3Q-8C.js → css-BgGQbvrZ.js} +0 -0
- /package/dist/web/assets/{csv-CpqjMGiA.js → csv-CNHmQBzs.js} +0 -0
- /package/dist/web/assets/{csv-parser-3umBA4NY.js → csv-parser-DWR24J17.js} +0 -0
- /package/dist/web/assets/{cue-DinoJ_5k.js → cue-ZXAKzwIy.js} +0 -0
- /package/dist/web/assets/{cypher-DzxqmkUW.js → cypher-4PL5bmT5.js} +0 -0
- /package/dist/web/assets/{d-BxNwJuEC.js → d-CEsV3RT2.js} +0 -0
- /package/dist/web/assets/{dark-plus-Clmv3V-0.js → dark-plus-NtFnYSDz.js} +0 -0
- /package/dist/web/assets/{dart-BWVtXD35.js → dart-CKHn4mDf.js} +0 -0
- /package/dist/web/assets/{data-grid-types-B4n51Fck.js → data-grid-types-CxU8oo-j.js} +0 -0
- /package/dist/web/assets/{dax-DsImuWgJ.js → dax-Cmkj1I_M.js} +0 -0
- /package/dist/web/assets/{desktop-B0jJuG2r.js → desktop-DJLalvPQ.js} +0 -0
- /package/dist/web/assets/{diff-CWqAAxoE.js → diff-BnS2wpj6.js} +0 -0
- /package/dist/web/assets/{dist-C0eNNfbt.js → dist-CuIdRyUB.js} +0 -0
- /package/dist/web/assets/{dist-B-EXdSYE.js → dist-uYjs3Wv6.js} +0 -0
- /package/dist/web/assets/{docker-CYFCJdIL.js → docker-CovNy7ml.js} +0 -0
- /package/dist/web/assets/{dotenv-CKDUjURg.js → dotenv-Gh4w3I6B.js} +0 -0
- /package/dist/web/assets/{dracula-CSoPspHE.js → dracula-NF0tAHXl.js} +0 -0
- /package/dist/web/assets/{dracula-soft-BuFjw5L1.js → dracula-soft-Cj_qBurK.js} +0 -0
- /package/dist/web/assets/{dream-maker-B6PdDRZu.js → dream-maker-CRuZPcl7.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-CMcXvD0G.js → emacs-lisp-BACmC9qj.js} +0 -0
- /package/dist/web/assets/{everforest-dark-cU8-wYzi.js → everforest-dark-Dd9av4QG.js} +0 -0
- /package/dist/web/assets/{everforest-light-KhorllqK.js → everforest-light-C-jsss-B.js} +0 -0
- /package/dist/web/assets/{external-link-B8BynhvY.js → external-link-B6Hl0U4X.js} +0 -0
- /package/dist/web/assets/{fennel-BCELZK87.js → fennel-BJA9XnL8.js} +0 -0
- /package/dist/web/assets/{file-exclamation-point-CZ4DiR75.js → file-exclamation-point-JzhTU2TX.js} +0 -0
- /package/dist/web/assets/{fish-BQc0y_jy.js → fish-CKlhNSc2.js} +0 -0
- /package/dist/web/assets/{fluent-BTINnwF6.js → fluent-wfglRk5D.js} +0 -0
- /package/dist/web/assets/{folder-symlink-B8XrtvQK.js → folder-symlink-Dx1hOk8X.js} +0 -0
- /package/dist/web/assets/{format-bytes-WVcFxJuT.js → format-bytes-utwHMTry.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-BQim9wDz.js → fortran-free-form-BlXCdid3.js} +0 -0
- /package/dist/web/assets/{gdscript-03w5u8un.js → gdscript-BwAVG6Et.js} +0 -0
- /package/dist/web/assets/{gdshader-Dp2daNzU.js → gdshader-DPc_T7ib.js} +0 -0
- /package/dist/web/assets/{genie-CoNcjPiD.js → genie-B-zZSLCX.js} +0 -0
- /package/dist/web/assets/{gherkin-NrPOSPQL.js → gherkin-CJ0ajpGs.js} +0 -0
- /package/dist/web/assets/{github-dark-z4RE9ZqA.js → github-dark-Dz8ZkzSP.js} +0 -0
- /package/dist/web/assets/{github-dark-default-DOKw-1cI.js → github-dark-default-CNUYo_kp.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-DJRNMkEA.js → github-dark-dimmed-241z_Ycx.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-Bj7jJ_Gs.js → github-dark-high-contrast-7r6yZZYH.js} +0 -0
- /package/dist/web/assets/{github-light-B4k_yMFu.js → github-light-C8gmZn-D.js} +0 -0
- /package/dist/web/assets/{github-light-default-BHc-Uzhs.js → github-light-default-GAEgq1eA.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-CHisIywL.js → github-light-high-contrast-3z91352E.js} +0 -0
- /package/dist/web/assets/{gleam-CfbypZ4q.js → gleam-C7vxW5zd.js} +0 -0
- /package/dist/web/assets/{gn-YoAjyhJa.js → gn-CyJIXwHP.js} +0 -0
- /package/dist/web/assets/{gnuplot-xVkc1f1c.js → gnuplot-CqLbGgPd.js} +0 -0
- /package/dist/web/assets/{go-GPCwP3Gu.js → go-DdDcK9aX.js} +0 -0
- /package/dist/web/assets/{groovy-Crh4Yww9.js → groovy-UYHggEqH.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-BluUFuDS.js → gruvbox-dark-hard-B4ierkJn.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-Cxk1FWQp.js → gruvbox-dark-medium-DYwkDSZ6.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-CHXMiFmj.js → gruvbox-dark-soft-CVL4P-Lz.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-B6eKPjAD.js → gruvbox-light-hard-BJ0LewUZ.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-pDwMm1ut.js → gruvbox-light-medium-CaqqjeGJ.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-Cxu6kHqK.js → gruvbox-light-soft-ByKMoVNr.js} +0 -0
- /package/dist/web/assets/{haskell-C6JKHjbK.js → haskell-BilYRVPp.js} +0 -0
- /package/dist/web/assets/{haxe-BuBNdF0a.js → haxe-Brs9Bj1s.js} +0 -0
- /package/dist/web/assets/{hcl-DZpDdGj2.js → hcl-ClrZpol-.js} +0 -0
- /package/dist/web/assets/{hjson-CBkhYRuo.js → hjson-9PvHnYMX.js} +0 -0
- /package/dist/web/assets/{hlsl-C5nMxnp9.js → hlsl-BbBk4vqV.js} +0 -0
- /package/dist/web/assets/{horizon-VAFvA6R6.js → horizon-E5Py0cny.js} +0 -0
- /package/dist/web/assets/{horizon-bright-BC0xn7T3.js → horizon-bright-DVCsNGEk.js} +0 -0
- /package/dist/web/assets/{houston-BxrUivmz.js → houston-JdFEax9Y.js} +0 -0
- /package/dist/web/assets/{hy-DLtOXoaH.js → hy-W_W3tLKT.js} +0 -0
- /package/dist/web/assets/{image-gallery-Gnf7TdCR.js → image-gallery-5IURd6db.js} +0 -0
- /package/dist/web/assets/{imba-BdpFXM2W.js → imba-CfAqJz7T.js} +0 -0
- /package/dist/web/assets/{ini-BInJO_T0.js → ini-C_3gvKWs.js} +0 -0
- /package/dist/web/assets/{java-Dxuddnkb.js → java-CKqtXZ-j.js} +0 -0
- /package/dist/web/assets/{javascript-CVamJfLY.js → javascript-C7gSEfae.js} +0 -0
- /package/dist/web/assets/{json-BLZ0ozYk.js → json-BoN6e11T.js} +0 -0
- /package/dist/web/assets/{json5-BnXzmMKs.js → json5-CgE7C8HW.js} +0 -0
- /package/dist/web/assets/{jsonc-CSgkSga7.js → jsonc-DjyoattY.js} +0 -0
- /package/dist/web/assets/{jsonl-CY5W2nF2.js → jsonl-lI82wGSw.js} +0 -0
- /package/dist/web/assets/{jsonnet-CEJYH3sF.js → jsonnet-CszEI8va.js} +0 -0
- /package/dist/web/assets/{jssm-CsJc7Uhk.js → jssm-CZXDlszb.js} +0 -0
- /package/dist/web/assets/{jsx-jCiRaHU7.js → jsx-BeRz0eEz.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-DfoA_GxX.js → kanagawa-dragon-BOmnUiyd.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-BOLYJpWd.js → kanagawa-lotus-ffU_P5w-.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-DfKlbT1G.js → kanagawa-wave-DKTJZrMu.js} +0 -0
- /package/dist/web/assets/{kdl-CmssIFTi.js → kdl-BWECJveH.js} +0 -0
- /package/dist/web/assets/{kotlin-BRsNgp6x.js → kotlin-DpVwBCSt.js} +0 -0
- /package/dist/web/assets/{kusto-T8X9jUab.js → kusto-BgVqjUZA.js} +0 -0
- /package/dist/web/assets/{laserwave-DkJMVJ8Z.js → laserwave-CEVCi8em.js} +0 -0
- /package/dist/web/assets/{lean-C65KGVPq.js → lean-CGyfRhLJ.js} +0 -0
- /package/dist/web/assets/{less-exIPdDlf.js → less-B0_H10Ku.js} +0 -0
- /package/dist/web/assets/{lib-mC1NZfuw.js → lib-hrC3bruJ.js} +0 -0
- /package/dist/web/assets/{libesm-DSutUtl1.js → libesm-DVpFmwJu.js} +0 -0
- /package/dist/web/assets/{light-plus-DiHUXAZt.js → light-plus-DqzMqPID.js} +0 -0
- /package/dist/web/assets/{llvm-CJBnEzFM.js → llvm-9ucnfNiF.js} +0 -0
- /package/dist/web/assets/{loader-circle-gSPVxfvH.js → loader-circle-Dpqcv0eS.js} +0 -0
- /package/dist/web/assets/{lock-Did-87UV.js → lock-DldbT31x.js} +0 -0
- /package/dist/web/assets/{log-DxMyUMuP.js → log-m6A2dOSj.js} +0 -0
- /package/dist/web/assets/{logo-CC9NKzca.js → logo-ByPcsC9P.js} +0 -0
- /package/dist/web/assets/{luau-Bw10I6zf.js → luau-DFCo8nBn.js} +0 -0
- /package/dist/web/assets/{make-C_6M90m6.js → make-C90w3MeA.js} +0 -0
- /package/dist/web/assets/{markdown-bR9_R-LF.js → markdown-DT0uywZG.js} +0 -0
- /package/dist/web/assets/{material-theme-BUx2Qujn.js → material-theme-BVUO93tJ.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-CRJrPgKm.js → material-theme-darker-xXgGDwnF.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-CUAYppPa.js → material-theme-lighter-BjStPzWB.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-DuINF1fg.js → material-theme-ocean-wFmSiKjy.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-W6_icGCj.js → material-theme-palenight-e-0CtRoe.js} +0 -0
- /package/dist/web/assets/{matlab-rfqn_b3z.js → matlab-BNaNQAUH.js} +0 -0
- /package/dist/web/assets/{mdx-Bh1B_YOJ.js → mdx-DfpVWAqO.js} +0 -0
- /package/dist/web/assets/{mermaid-Dxf6Ya2k.js → mermaid-Bpq7wFCP.js} +0 -0
- /package/dist/web/assets/{min-dark-CivZ2L0h.js → min-dark-CL2Y0WH6.js} +0 -0
- /package/dist/web/assets/{min-light-B1Ykqtma.js → min-light-DOgr_IdP.js} +0 -0
- /package/dist/web/assets/{mipsasm-DynHaM0d.js → mipsasm-C9ie2zlB.js} +0 -0
- /package/dist/web/assets/{mojo-Clw5PbGH.js → mojo-Md2A2sJy.js} +0 -0
- /package/dist/web/assets/{monitor-Dv8vn_6O.js → monitor-4vWfqzuN.js} +0 -0
- /package/dist/web/assets/{monokai-DFIooFZT.js → monokai-nbXIEySB.js} +0 -0
- /package/dist/web/assets/{moonbit-DnBSZWGM.js → moonbit-CJ-T-s4A.js} +0 -0
- /package/dist/web/assets/{move-CCcL2PfL.js → move-DwEehLIK.js} +0 -0
- /package/dist/web/assets/{narrat-BDarg5Lk.js → narrat-CuF4sIKr.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-DrZOeCNW.js → nextflow-groovy-1dYhfPfq.js} +0 -0
- /package/dist/web/assets/{night-owl-BnZZCK7v.js → night-owl-gZS5mfzY.js} +0 -0
- /package/dist/web/assets/{night-owl-light-CEwBsVHS.js → night-owl-light-DDAster5.js} +0 -0
- /package/dist/web/assets/{nix-amCzOPMR.js → nix-BlbEYs2c.js} +0 -0
- /package/dist/web/assets/{nord-BIs2BC6q.js → nord-BRiPb8Vy.js} +0 -0
- /package/dist/web/assets/{nushell-Y02fo4yw.js → nushell-DgWicB1i.js} +0 -0
- /package/dist/web/assets/{objective-c-7xOZtWW1.js → objective-c-D8mPZ42Y.js} +0 -0
- /package/dist/web/assets/{objective-cpp-eVtjUvVB.js → objective-cpp-B0pNXzuj.js} +0 -0
- /package/dist/web/assets/{ocaml-Bi6ZwqG4.js → ocaml-lQd7aK5d.js} +0 -0
- /package/dist/web/assets/{odin-8xf1Y0S9.js → odin-CxtIOBjm.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-hVv-_y_b.js → one-dark-pro-T_UFnWfe.js} +0 -0
- /package/dist/web/assets/{one-light-CJJ3MtHt.js → one-light-LBVqwyV-.js} +0 -0
- /package/dist/web/assets/{openscad-B2HcywLk.js → openscad-C5dnS6J7.js} +0 -0
- /package/dist/web/assets/{pascal-C7fllV_7.js → pascal-CmWCWQML.js} +0 -0
- /package/dist/web/assets/{pkl-qvLC1IWP.js → pkl-BWWOtDQk.js} +0 -0
- /package/dist/web/assets/{plastic-BT1wHZv2.js → plastic-ekhupckF.js} +0 -0
- /package/dist/web/assets/{play-4MZi1Axp.js → play-CpCAoQRZ.js} +0 -0
- /package/dist/web/assets/{plsql-1MsmdmJD.js → plsql-BPsDHXx4.js} +0 -0
- /package/dist/web/assets/{plus-BhMZqFsN.js → plus-BR0qewJI.js} +0 -0
- /package/dist/web/assets/{po-BigskytD.js → po-BX2FiuW2.js} +0 -0
- /package/dist/web/assets/{poimandres-DuklniF7.js → poimandres-DzXOdZcN.js} +0 -0
- /package/dist/web/assets/{polar-e2pjN_sp.js → polar-DDRXgJzG.js} +0 -0
- /package/dist/web/assets/{portal-container-context-Cm5HipFr.js → portal-container-context-a399Hd9F.js} +0 -0
- /package/dist/web/assets/{postcss-DtAeYjWw.js → postcss-BHW6XBhx.js} +0 -0
- /package/dist/web/assets/{powerquery-Cr2PtehI.js → powerquery-B89c2NBb.js} +0 -0
- /package/dist/web/assets/{powershell-BxFTGLJ4.js → powershell-Dvxrd3Tu.js} +0 -0
- /package/dist/web/assets/{prisma-COWJiwdZ.js → prisma-lMK8EFLU.js} +0 -0
- /package/dist/web/assets/{prolog-DFvGKRx6.js → prolog-D0YrtMG4.js} +0 -0
- /package/dist/web/assets/{proto-CcK80lFt.js → proto-J0mM25aw.js} +0 -0
- /package/dist/web/assets/{puppet-BF60bZ4F.js → puppet-CUT0xkhh.js} +0 -0
- /package/dist/web/assets/{purescript-zL_69MzU.js → purescript-CoJWSGRp.js} +0 -0
- /package/dist/web/assets/{python-CIwKhfb_.js → python-BBrmuz6P.js} +0 -0
- /package/dist/web/assets/{qmldir-CXNe04Lo.js → qmldir-CUtzs4Q7.js} +0 -0
- /package/dist/web/assets/{qss-C8lAWe6G.js → qss-Dm1RZ8aL.js} +0 -0
- /package/dist/web/assets/{r-CL5ZVL3_.js → r-BGP7qZTe.js} +0 -0
- /package/dist/web/assets/{racket-BBfh0uh0.js → racket-K_6U1lqJ.js} +0 -0
- /package/dist/web/assets/{raku-o6E8R5Q3.js → raku-bNSiZeW_.js} +0 -0
- /package/dist/web/assets/{react-CBTnT4sY.js → react-X4jUntEf.js} +0 -0
- /package/dist/web/assets/{red-GomRF2Xp.js → red-uVeIEF0f.js} +0 -0
- /package/dist/web/assets/{refresh-cw-B06yaBrW.js → refresh-cw-B9iv7csb.js} +0 -0
- /package/dist/web/assets/{reg-_ffDIvx9.js → reg-DSm9GWjh.js} +0 -0
- /package/dist/web/assets/{regexp-Bl5sF65W.js → regexp-CljRm_j_.js} +0 -0
- /package/dist/web/assets/{rel-DgMdqDuj.js → rel-d3JcopZg.js} +0 -0
- /package/dist/web/assets/{resolve-theme-CRt_7wpd.js → resolve-theme-DLKmm-lE.js} +0 -0
- /package/dist/web/assets/{riscv-8b3-Ye4X.js → riscv-CJmaELjT.js} +0 -0
- /package/dist/web/assets/{ron-DrmU1KHP.js → ron-D0g9B1rW.js} +0 -0
- /package/dist/web/assets/{rose-pine-C_-E2tF1.js → rose-pine-DOrD2BSH.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-DEfhNlFH.js → rose-pine-dawn-CzZQ-LCF.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-BdvX3gdn.js → rose-pine-moon-CdmsYp7C.js} +0 -0
- /package/dist/web/assets/{rosmsg-B6GhGpd5.js → rosmsg-CmDth6pO.js} +0 -0
- /package/dist/web/assets/{rotate-ccw-COXu_9oj.js → rotate-ccw-DnuPzJRE.js} +0 -0
- /package/dist/web/assets/{rust-DmC97M1-.js → rust-DGdP6jiR.js} +0 -0
- /package/dist/web/assets/{sass-B0tJOHLM.js → sass-Dkgt2FKZ.js} +0 -0
- /package/dist/web/assets/{save-CtIGOjmx.js → save-DxonD4y1.js} +0 -0
- /package/dist/web/assets/{scala-n8zaFOXv.js → scala-BQLTON6C.js} +0 -0
- /package/dist/web/assets/{scheme-BKD9ztBU.js → scheme-DKlxUGCi.js} +0 -0
- /package/dist/web/assets/{sdbl-D8gB5-rS.js → sdbl-C3iZJQeZ.js} +0 -0
- /package/dist/web/assets/{send-BlyNJAzy.js → send-B-t5Mz26.js} +0 -0
- /package/dist/web/assets/{shallow-DYo6goJ4.js → shallow-jwPzEunk.js} +0 -0
- /package/dist/web/assets/{shellscript-BfXlf_Ao.js → shellscript-BRu-ZQAx.js} +0 -0
- /package/dist/web/assets/{shield-check-B0iGUiOK.js → shield-check-CdTiK1j2.js} +0 -0
- /package/dist/web/assets/{shield-off-BNLWBhcq.js → shield-off-DuetkXKM.js} +0 -0
- /package/dist/web/assets/{slack-dark-CylEddse.js → slack-dark-qkm_u2zz.js} +0 -0
- /package/dist/web/assets/{slack-ochin-w8wg8vuC.js → slack-ochin-BP8_8e4m.js} +0 -0
- /package/dist/web/assets/{smalltalk-hiAXA64u.js → smalltalk-Bnz4gt07.js} +0 -0
- /package/dist/web/assets/{snazzy-light-BwlV3Xty.js → snazzy-light-fbPZg5io.js} +0 -0
- /package/dist/web/assets/{solarized-dark-vmNGdykz.js → solarized-dark-CM0vTRoi.js} +0 -0
- /package/dist/web/assets/{solarized-light-DGv_0JXq.js → solarized-light-C4_W9mvs.js} +0 -0
- /package/dist/web/assets/{solidity-Y7QSH0B1.js → solidity-Bz6v1PoD.js} +0 -0
- /package/dist/web/assets/{splunk-BtgpLjHi.js → splunk-DyNxu-0i.js} +0 -0
- /package/dist/web/assets/{sql-CRHYhOia.js → sql-DXRL1gw1.js} +0 -0
- /package/dist/web/assets/{ssh-config-DOqSs3au.js → ssh-config-2z1QHuOG.js} +0 -0
- /package/dist/web/assets/{stylus-crCAEaBT.js → stylus-D4I-VaeY.js} +0 -0
- /package/dist/web/assets/{swift-eE3rT7TC.js → swift-C_2G-tIc.js} +0 -0
- /package/dist/web/assets/{synthwave-84-BWy14LBX.js → synthwave-84-osBjChRP.js} +0 -0
- /package/dist/web/assets/{system-verilog-DC0-DZM4.js → system-verilog-zQP2qPNU.js} +0 -0
- /package/dist/web/assets/{systemd-to5p6N8I.js → systemd-UGfjRSBM.js} +0 -0
- /package/dist/web/assets/{table-Bbc783ko.js → table-FLU8ei8b.js} +0 -0
- /package/dist/web/assets/{talonscript-DoTcU8wm.js → talonscript-C1XrFPwo.js} +0 -0
- /package/dist/web/assets/{tasl-cNZvUs95.js → tasl-B5raVn43.js} +0 -0
- /package/dist/web/assets/{tcl-mZE8sZH8.js → tcl-3-vbeMIc.js} +0 -0
- /package/dist/web/assets/{terraform-CqHPpIfu.js → terraform-a8VXZDm9.js} +0 -0
- /package/dist/web/assets/{text-wrap-CBT4Gxtg.js → text-wrap-B05VGRa-.js} +0 -0
- /package/dist/web/assets/{tokyo-night-Tp2rTIZd.js → tokyo-night-4SRhf-Hz.js} +0 -0
- /package/dist/web/assets/{toml-CU_vscgn.js → toml-B4N0i8wT.js} +0 -0
- /package/dist/web/assets/{trash-2-Bk4yYlck.js → trash-2-CTXlTflF.js} +0 -0
- /package/dist/web/assets/{triangle-alert-B8EYecpe.js → triangle-alert-BRq0VAfd.js} +0 -0
- /package/dist/web/assets/{tsv-ik7iFB9D.js → tsv-DA5k9Lvw.js} +0 -0
- /package/dist/web/assets/{tsx-BMvvMKiv.js → tsx-D9SZ8owC.js} +0 -0
- /package/dist/web/assets/{turtle-DBqKCq9o.js → turtle-BTHTvyY_.js} +0 -0
- /package/dist/web/assets/{typescript-DXJDmMZZ.js → typescript-DYzV9p5L.js} +0 -0
- /package/dist/web/assets/{typespec-CBseCMtZ.js → typespec-BC15eh7F.js} +0 -0
- /package/dist/web/assets/{typst-4oY25U9y.js → typst-BJUqzA6D.js} +0 -0
- /package/dist/web/assets/{use-is-mobile-D1weanbj.js → use-is-mobile-DFWViA4S.js} +0 -0
- /package/dist/web/assets/{utils-BNRH507E.js → utils-C9OjCrou.js} +0 -0
- /package/dist/web/assets/{v-BXaZ_1oy.js → v-50Lps6oA.js} +0 -0
- /package/dist/web/assets/{vala-BImWJE7z.js → vala-8wiUvvrD.js} +0 -0
- /package/dist/web/assets/{vb-CLfj2_sy.js → vb-BlgPLt6B.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-_yyu5n32.js → vendor-xterm-BZOrwF_F.js} +0 -0
- /package/dist/web/assets/{verilog-BWa8Ed2b.js → verilog-CD9pVW5p.js} +0 -0
- /package/dist/web/assets/{vesper-BGj1cg6m.js → vesper-BrivW4PT.js} +0 -0
- /package/dist/web/assets/{vhdl-mByK7mlx.js → vhdl-Bunxfa7N.js} +0 -0
- /package/dist/web/assets/{viml-CugmlW5g.js → viml-DndUb2qM.js} +0 -0
- /package/dist/web/assets/{vitesse-black-vPdpy9TL.js → vitesse-black-BurZSkxW.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-BuQK3dUh.js → vitesse-dark-8UShspzC.js} +0 -0
- /package/dist/web/assets/{vitesse-light-B3nz1XL9.js → vitesse-light-DghCAZ90.js} +0 -0
- /package/dist/web/assets/{vyper-DH_GTIHG.js → vyper-b-Clr-8u.js} +0 -0
- /package/dist/web/assets/{wasm-DfVbVJlW.js → wasm-C5v2T06Y.js} +0 -0
- /package/dist/web/assets/{wasm-C-LF9GjX.js → wasm-CpcKStkw.js} +0 -0
- /package/dist/web/assets/{wenyan-CzQkHyHx.js → wenyan-Dz2srLZR.js} +0 -0
- /package/dist/web/assets/{wgsl-Du_M1DW9.js → wgsl-CeD1-dRb.js} +0 -0
- /package/dist/web/assets/{wikitext-BWN_w5S7.js → wikitext-CyeS-9FH.js} +0 -0
- /package/dist/web/assets/{wit-BR13v5HD.js → wit-Kr-q-qBQ.js} +0 -0
- /package/dist/web/assets/{wolfram-BoXxp_Xl.js → wolfram-BKm8bmln.js} +0 -0
- /package/dist/web/assets/{x-nhriOX9o.js → x-YDu4m2Iq.js} +0 -0
- /package/dist/web/assets/{yaml-DpR_EF-d.js → yaml-2XSxomjM.js} +0 -0
- /package/dist/web/assets/{zenscript-C-EdUtUd.js → zenscript-CKIS34vf.js} +0 -0
- /package/dist/web/assets/{zig-BD4eA5nU.js → zig-DdHGNllX.js} +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# Integrations (PPMBot, Jira)
|
|
2
|
+
|
|
3
|
+
> Part of the [PPM system architecture](../system-architecture.md).
|
|
4
|
+
|
|
5
|
+
## PPMBot Coordinator Service Layer (Telegram-based Team Leader)
|
|
6
|
+
**Component:** PPMBot coordinator orchestrator + delegation executor
|
|
7
|
+
|
|
8
|
+
**Responsibilities:**
|
|
9
|
+
- Manage single persistent coordinator session per Telegram chat in `~/.ppm/bot/` workspace
|
|
10
|
+
- Route incoming Telegram messages to coordinator (ask/answer) or delegation tracking
|
|
11
|
+
- Decide when to answer directly vs. delegate to subagents (based on project context)
|
|
12
|
+
- Execute delegated tasks in isolated project sessions
|
|
13
|
+
- Track task status and report results back to Telegram
|
|
14
|
+
- Format responses as Telegram HTML with progressive message editing
|
|
15
|
+
|
|
16
|
+
**Architecture:**
|
|
17
|
+
```
|
|
18
|
+
Telegram → PPMBotTelegramService (polling) → PPMBotService (orchestrator)
|
|
19
|
+
↓
|
|
20
|
+
PPMBotSessionManager (coordinator session per chat)
|
|
21
|
+
coordinatorSession.id → chatService.sendMessage()
|
|
22
|
+
Task Poller (5s interval)
|
|
23
|
+
↓
|
|
24
|
+
executeDelegation(taskId, telegram, providerId)
|
|
25
|
+
├─ getBotTask(taskId) → prompt
|
|
26
|
+
├─ chatService.createSession(providerId, projectPath)
|
|
27
|
+
├─ run async generator (abort, 900s timeout)
|
|
28
|
+
└─ updateBotTaskStatus(taskId, "completed", {result})
|
|
29
|
+
↓
|
|
30
|
+
telegram.sendMessage(chatId, result summary)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Services (src/services/ppmbot/):**
|
|
34
|
+
- **PPMBotService** — Lifecycle (start/stop), message queue, Telegram polling loop, task poller loop
|
|
35
|
+
- **PPMBotSessionManager** — Coordinator session cache per chatID, project resolver (case-insensitive, prefix match)
|
|
36
|
+
- **PPMBotTelegramService** — Telegram Bot API (getUpdates polling, sendMessage, editMessage, setTyping)
|
|
37
|
+
- **PPMBotMemoryService** — SQLite project memories, contextual recall
|
|
38
|
+
- **executeDelegation()** — Task execution in isolated session, result capture, timeout/abort handling
|
|
39
|
+
- **PPMBotFormatterService** — Markdown → Telegram HTML, 4096-char chunking
|
|
40
|
+
- **PPMBotStreamerService** — ChatEvent → progressive Telegram message edits (1s throttle)
|
|
41
|
+
|
|
42
|
+
**Coordinator Identity (Persistent Cross-Provider):**
|
|
43
|
+
- Location: `~/.ppm/bot/coordinator.md` (loaded on startup, cached in `coordinatorIdentity`)
|
|
44
|
+
- Role definition: Team leader, project coordinator, decision-maker
|
|
45
|
+
- Decision framework: Answer directly (no project context) vs. Delegate (file access needed)
|
|
46
|
+
- Coordination tools: Bash-safe CLI commands (`ppm bot delegate`, `ppm bot task-status`, etc.)
|
|
47
|
+
- Cross-provider: Identity text injected as XML context block, works with Claude SDK + CLI providers
|
|
48
|
+
|
|
49
|
+
**Delegation Flow:**
|
|
50
|
+
1. User asks task in Telegram
|
|
51
|
+
2. Coordinator decides: delegate? → yes
|
|
52
|
+
3. Coordinator calls bash: `ppm bot delegate --chat <chatId> --project <name> --prompt "<enriched>"`
|
|
53
|
+
4. CLI creates `bot_tasks` row, returns taskId
|
|
54
|
+
5. Service tells user: "Working on it, I'll notify you when done"
|
|
55
|
+
6. Background poller (5s) detects pending task
|
|
56
|
+
7. Executes: `chatService.createSession()` in target project
|
|
57
|
+
8. Streams response, captures summary + full output
|
|
58
|
+
9. Updates task status → "completed"
|
|
59
|
+
10. Sends Telegram notification with result
|
|
60
|
+
|
|
61
|
+
**Task Execution (Isolation & Safety):**
|
|
62
|
+
- Each task = fresh isolated session (no shared context)
|
|
63
|
+
- Timeout: 900s default (configurable per task)
|
|
64
|
+
- Abort: AbortController on timeout, can be canceled mid-execution
|
|
65
|
+
- Result capture: Both summary (for notification) and full text (for detailed review)
|
|
66
|
+
- Error handling: Task status → "failed", error message stored, user notified
|
|
67
|
+
|
|
68
|
+
**Database Schema (v14):**
|
|
69
|
+
- `bot_tasks` — id (UUID), chatId, projectName, projectPath, prompt, status, resultSummary, resultFull, sessionId, error, reported, timeoutMs, createdAt, startedAt, completedAt
|
|
70
|
+
- Indexes: `idx_bot_tasks_status` (fast poller lookup), `idx_bot_tasks_chat` (history queries)
|
|
71
|
+
|
|
72
|
+
**Key Design Decisions:**
|
|
73
|
+
1. **Single coordinator session** — Per chat, persistent, one identity (vs. per-task sessions in ClawBot)
|
|
74
|
+
2. **Delegation via CLI** — Coordinator calls bash commands (safer than direct DB writes, auditable)
|
|
75
|
+
3. **Isolated task execution** — Each delegated task spawns fresh session (no context bleed)
|
|
76
|
+
4. **Background polling** — Task execution decoupled from message handler (non-blocking)
|
|
77
|
+
5. **Result summary + full** — Notification shows short summary; user can fetch full output via CLI
|
|
78
|
+
6. **Cross-provider identity** — Single `coordinator.md` works with any AI provider
|
|
79
|
+
7. **Bash-safe tools only** — Coordinator restricted to Bash, Read, Write, Edit, Glob, Grep (safe delegation)
|
|
80
|
+
|
|
81
|
+
**CLI Expansion (ppm bot commands):**
|
|
82
|
+
```
|
|
83
|
+
ppm bot delegate --chat <id> --project <name> --prompt "<text>" # Create task
|
|
84
|
+
ppm bot task-status <id> # Check status
|
|
85
|
+
ppm bot task-result <id> # Get full output
|
|
86
|
+
ppm bot tasks [--chat <id>] # List recent
|
|
87
|
+
ppm bot project list # Available projects
|
|
88
|
+
ppm bot project current # Active project
|
|
89
|
+
ppm bot project switch <name> # Switch project
|
|
90
|
+
ppm bot session new <title> # Create session
|
|
91
|
+
ppm bot session list # List sessions
|
|
92
|
+
ppm bot session resume <id> # Resume session
|
|
93
|
+
ppm bot session stop <id> # Stop session
|
|
94
|
+
ppm bot status # Bot health
|
|
95
|
+
ppm bot version # PPM version
|
|
96
|
+
ppm bot restart # Restart service
|
|
97
|
+
ppm bot help # Help
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Settings UI (ppmbot-settings-section.tsx):**
|
|
101
|
+
- Enable/disable PPMBot
|
|
102
|
+
- Paired Telegram chats (approval management)
|
|
103
|
+
- Default project selection
|
|
104
|
+
- System prompt customization
|
|
105
|
+
- Task auto-refresh (poll interval, max history)
|
|
106
|
+
- Delegated tasks panel (status, result preview, delete)
|
|
107
|
+
|
|
108
|
+
**Legacy `clawbot` naming:** PPMBot replaced an earlier bot called ClawBot, but the config key and
|
|
109
|
+
its REST surface were never renamed. `config.clawbot` (typed `PPMBotConfig`) and
|
|
110
|
+
`GET|PUT /api/settings/clawbot` configure **PPMBot** — toggling `enabled` there starts or stops
|
|
111
|
+
`ppmbotService`. The old `ClawBotService` / `ClawBotSessionService` / `ClawBotMemoryService` /
|
|
112
|
+
`ClawBotStreamerService` no longer exist; the implementation is `src/services/ppmbot/*`. Treat
|
|
113
|
+
`clawbot` purely as a backward-compatible key name, not as a separate subsystem.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Jira Watcher Auto-Debug Service
|
|
118
|
+
**Component:** Jira Cloud REST API poller + direct Claude debug session orchestrator
|
|
119
|
+
|
|
120
|
+
**Responsibilities:**
|
|
121
|
+
- Poll Jira Cloud per-project on configurable interval (30s–60m)
|
|
122
|
+
- Match issues via JQL filters (status, project key, priority, etc.)
|
|
123
|
+
- Auto-queue or manually trigger direct Claude debug sessions (no bot_task middleman)
|
|
124
|
+
- Manage concurrency: max 2 concurrent, max 1 per project
|
|
125
|
+
- Track results (pending/queued/running/done/failed) with unread status
|
|
126
|
+
- Notify via WS toast + Telegram when analysis completes
|
|
127
|
+
- Rate-limit aware (tracks Jira API quota, auto-backoff 429 responses)
|
|
128
|
+
|
|
129
|
+
**Architecture:**
|
|
130
|
+
```
|
|
131
|
+
Jira Cloud API ← JiraWatcherService (poller, 30s–60m intervals per watcher)
|
|
132
|
+
├─ searchIssues(jql) → issue list
|
|
133
|
+
├─ insertResult() → SQLite jira_watch_results
|
|
134
|
+
└─ jiraDebugService.enqueue() → concurrency queue
|
|
135
|
+
|
|
136
|
+
JiraDebugSessionService (concurrency queue processor)
|
|
137
|
+
├─ enqueue(resultId, promptOverride?) → validate + queue
|
|
138
|
+
├─ processQueue() — respects MAX_CONCURRENT=2, MAX_PER_PROJECT=1
|
|
139
|
+
├─ runDebugSession()
|
|
140
|
+
│ ├─ chatService.createSession(projectPath) — new isolated session
|
|
141
|
+
│ ├─ chatService.sendMessage(prompt) — send with bypassPermissions
|
|
142
|
+
│ ├─ capture lastAssistantText (max 500 chars)
|
|
143
|
+
│ └─ updateResultStatus() + notificationService.broadcastWs("jira:debug_complete")
|
|
144
|
+
└─ cancelDebug(resultId) — abort running session
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Services (src/services/):**
|
|
148
|
+
- **JiraConfigService** — Config CRUD, AES-256 token encryption/decryption, per-project setup
|
|
149
|
+
- **JiraWatcherDbService** — Watchers + results table queries, enabled/disabled toggle, last polled tracking
|
|
150
|
+
- **JiraApiClient** — Jira Cloud REST v3 (search, getIssue, transitions, test connection), rate limit state, backoff logic
|
|
151
|
+
- **JiraWatcherService** — Main poller, timer management (startAll, startWatcher, stopWatcher, pollWatcher), prompt templating, session enqueueing
|
|
152
|
+
- **JiraDebugSessionService** — Concurrency queue, session lifecycle, timeout management, abort handling
|
|
153
|
+
|
|
154
|
+
**Database Schema (v19):**
|
|
155
|
+
- `jira_config` — id, project_id (FK), base_url, email, api_token_encrypted, created_at
|
|
156
|
+
- `jira_watchers` — id, jira_config_id (FK), name, jql, prompt_template, enabled, mode ("debug"|"notify"), interval_ms, last_polled_at, created_at
|
|
157
|
+
- `jira_watch_results` — id, watcher_id, issue_key, issue_summary, issue_updated, session_id (FK chat_sessions.id), status ("pending"|"queued"|"running"|"done"|"failed"), ai_summary, source ("watcher"|"manual"), triggered_by ("auto"|"manual"), read_at (nullable), deleted, created_at
|
|
158
|
+
|
|
159
|
+
**API Routes (src/server/routes/jira*.ts):**
|
|
160
|
+
```
|
|
161
|
+
POST /api/jira/config — Create/update config (baseUrl, email, token)
|
|
162
|
+
GET /api/jira/config — Get config for active project
|
|
163
|
+
DELETE /api/jira/config — Delete config
|
|
164
|
+
POST /api/jira/config/test — Test Jira connection
|
|
165
|
+
GET /api/jira/watchers — List watchers for config
|
|
166
|
+
POST /api/jira/watchers — Create watcher (name, jql, mode, interval)
|
|
167
|
+
PATCH /api/jira/watchers/:id — Update watcher
|
|
168
|
+
DELETE /api/jira/watchers/:id — Delete watcher (soft delete results)
|
|
169
|
+
POST /api/jira/watchers/:id/enable — Enable/disable watcher
|
|
170
|
+
POST /api/jira/watchers/:id/poll — Trigger poll now
|
|
171
|
+
GET /api/jira/results — List results (paginated, filterable)
|
|
172
|
+
POST /api/jira/results/:id/debug — Manually trigger debug for result (with optional prompt override)
|
|
173
|
+
POST /api/jira/results/:id/read — Mark result as read
|
|
174
|
+
DELETE /api/jira/results/:id — Delete result (soft delete)
|
|
175
|
+
GET /api/jira/search — Search Jira (for filter builder UI)
|
|
176
|
+
GET /api/jira/ticket/:key — Get full ticket details
|
|
177
|
+
GET /api/jira/metadata — Fetch projects, issue types, priorities, statuses
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**CLI Commands (src/cli/commands/jira*.ts):**
|
|
181
|
+
```
|
|
182
|
+
ppm jira config set <project> --url <url> --email <email> --token <token>
|
|
183
|
+
ppm jira config show <project>
|
|
184
|
+
ppm jira config remove <project>
|
|
185
|
+
ppm jira config test <project>
|
|
186
|
+
ppm jira watch add <project> <name> --jql <jql> [--mode debug|notify] [--interval 300000]
|
|
187
|
+
ppm jira watch list <project>
|
|
188
|
+
ppm jira watch enable/disable <project> <watcherId>
|
|
189
|
+
ppm jira watch remove <project> <watcherId>
|
|
190
|
+
ppm jira watch test <project> <watcherId>
|
|
191
|
+
ppm jira watch pull <project> <watcherId>
|
|
192
|
+
ppm jira results list <project> [--limit 50]
|
|
193
|
+
ppm jira results delete <project> <resultId>
|
|
194
|
+
ppm jira track <issue-key> — Manually track ticket (insert result, queue debug)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**Frontend (src/web/components/jira/):**
|
|
198
|
+
- **jira-settings-tab.tsx** — Config form, test button, token input
|
|
199
|
+
- **jira-filter-builder.tsx** — JQL builder UI (projects, issue types, priorities, statuses, custom JQL)
|
|
200
|
+
- **jira-watcher-list.tsx** — List watchers, enable/disable, edit, delete, poll now, interval controls
|
|
201
|
+
- **jira-results-panel.tsx** — Results table (issue key, status, summary, AI summary), unread badge, delete, manual debug button
|
|
202
|
+
- **jira-debug-prompt-dialog.tsx** — Modal for prompt override when manually triggering debug
|
|
203
|
+
- **jira-ticket-detail.tsx** — Modal with full ticket, AI analysis, debug status
|
|
204
|
+
- **jira-store.ts** — Zustand (configs, watchers, results, filters, settings, unread count)
|
|
205
|
+
|
|
206
|
+
**Key Design Decisions:**
|
|
207
|
+
1. **Direct Claude sessions** — Replaced bot_task flow with direct `chatService.sendMessage()` (simpler, faster, no task overhead)
|
|
208
|
+
2. **Concurrency queue** — Max 2 concurrent globally, max 1 per project (prevents resource starvation, respects project context)
|
|
209
|
+
3. **Manual debug trigger** — Users can override watcher prompt and manually queue debug for any pending result
|
|
210
|
+
4. **Unread tracking** — `read_at` column marks when user views result, UI shows unread badge count
|
|
211
|
+
5. **Prompt templating** — Support {issue_key}, {summary}, {description}, {status}, {priority} placeholders in watcher templates
|
|
212
|
+
6. **Timeout protection** — 10-minute timeout with AbortController graceful cleanup and error capture
|
|
213
|
+
7. **WS notifications** — `jira:debug_complete` event streamed to UI for instant toast feedback
|
|
214
|
+
8. **Soft deletes** — Results marked deleted=1 (preserve history, don't lose tracking)
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
# Workspace & UI
|
|
2
|
+
|
|
3
|
+
> Part of the [PPM system architecture](../system-architecture.md).
|
|
4
|
+
|
|
5
|
+
## File Service & Filtering (Lazy-Load Tree, Palette Index)
|
|
6
|
+
|
|
7
|
+
**Component:** FileFilterService + API endpoints `/files/list`, `/files/index`, settings endpoints
|
|
8
|
+
|
|
9
|
+
**Overview:** Provides efficient file discovery with VS Code-style glob filtering and gitignore support. Three-layer filter precedence enforces consistent exclude patterns across tree navigation and search indexing.
|
|
10
|
+
|
|
11
|
+
**Filter Precedence (evaluated low-to-high):**
|
|
12
|
+
1. **Hardcoded defaults** — `node_modules/**`, `.git`, `.env*` (always excluded, cannot override)
|
|
13
|
+
2. **Global config** — `files.exclude`, `files.searchExclude`, `files.useIgnoreFiles` (applies to all projects)
|
|
14
|
+
3. **Per-project override** — Project-scoped settings (DB: `projects.settings` JSON, schema v21) override global
|
|
15
|
+
|
|
16
|
+
**API Endpoints:**
|
|
17
|
+
```
|
|
18
|
+
GET /api/project/:name/files/list?path=<rel>
|
|
19
|
+
→ 1-level directory children with gitignore decoration (isIgnored field)
|
|
20
|
+
→ { items: [{ name, type, isDir, isIgnored }], ... }
|
|
21
|
+
|
|
22
|
+
GET /api/project/:name/files/index
|
|
23
|
+
→ Flat full-project file list (cached in memory, watcher-invalidated)
|
|
24
|
+
→ { files: [{ path, isIgnored }], ... }
|
|
25
|
+
|
|
26
|
+
GET /api/settings/files
|
|
27
|
+
→ Global file filter config (all projects)
|
|
28
|
+
→ { filesExclude: [], searchExclude: [], useIgnoreFiles: bool }
|
|
29
|
+
|
|
30
|
+
PATCH /api/settings/files
|
|
31
|
+
→ Update global config (partial: only specified fields)
|
|
32
|
+
→ Validates arrays ≤200 items, filters non-string patterns
|
|
33
|
+
|
|
34
|
+
GET /api/project/:name/settings
|
|
35
|
+
→ Per-project settings (includes file filter overrides)
|
|
36
|
+
→ { filesExclude?: [], searchExclude?: [], useIgnoreFiles?: bool, ... }
|
|
37
|
+
|
|
38
|
+
PATCH /api/project/:name/settings
|
|
39
|
+
→ Per-project override (stored in projects.settings JSON, schema v21)
|
|
40
|
+
→ Same validation as global, caches invalidation on write
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Filtering Model:**
|
|
44
|
+
|
|
45
|
+
| Config | Applies To | Validation | Notes |
|
|
46
|
+
|--------|-----------|------------|-------|
|
|
47
|
+
| `filesExclude` | Tree navigation | Glob patterns (max 200) | Hides from tree explorer |
|
|
48
|
+
| `searchExclude` | Index + palette search | Glob patterns (max 200) | Hides from search results |
|
|
49
|
+
| `useIgnoreFiles` | Both (when true) | Boolean | Include `.gitignore` + `.git/info/exclude` in filtering |
|
|
50
|
+
|
|
51
|
+
**Frontend Integration:**
|
|
52
|
+
- `useFileStore()` hook manages lazy-loading: `loadRoot()`, `loadChildren()`, `loadIndex()`
|
|
53
|
+
- AbortController pool cancels pending requests on project switch
|
|
54
|
+
- File tree auto-expands root (1 level), children load on-demand with spinner
|
|
55
|
+
- Command palette + chat file-picker switched from tree-flattening to `fileIndex` from store
|
|
56
|
+
- "Indexing project…" hint shown when `loadIndex()` is pending
|
|
57
|
+
|
|
58
|
+
**Server-Side Implementation:**
|
|
59
|
+
- `FileFilterService.mergeFilters()` — Combine hardcoded + global + project overrides with precedence
|
|
60
|
+
- `FileFilterService.isPathIgnored()` — Check if path matches any exclude pattern (gitignore if enabled)
|
|
61
|
+
- `FileService.list()` — 1-level enumeration with `isIgnored` field computed per item
|
|
62
|
+
- In-memory `indexCache` (Map: projectName → FileIndex) invalidated by `fs.watch` (file changes) + manual `invalidateIndexCache()` calls
|
|
63
|
+
- WS `file:changed` events routed to `invalidateFolder()` or `invalidateIndex()` depending on scope
|
|
64
|
+
|
|
65
|
+
**Database Schema (v21+):**
|
|
66
|
+
```typescript
|
|
67
|
+
// projects table gains:
|
|
68
|
+
settings: TEXT // JSON: { filesExclude?, searchExclude?, useIgnoreFiles? }
|
|
69
|
+
|
|
70
|
+
// Example:
|
|
71
|
+
projects.settings = JSON.stringify({
|
|
72
|
+
filesExclude: ["**/.venv", "**/*.pyc"],
|
|
73
|
+
searchExclude: ["**/node_modules"],
|
|
74
|
+
useIgnoreFiles: false
|
|
75
|
+
})
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Deprecated:** `/api/project/:name/files/tree` (marked @deprecated, still functional for backward compat)
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Project Workspace Management
|
|
83
|
+
|
|
84
|
+
### Keep-Alive Pattern (v2.0+)
|
|
85
|
+
When switching projects, workspaces are preserved instead of destroyed:
|
|
86
|
+
1. **Workspace Mount State**: Each project's UI (tabs, terminal xterm DOM, file selections) remains mounted in the DOM
|
|
87
|
+
2. **Visibility Toggle**: CSS `display: none/block` hides/shows workspaces instead of React unmounting
|
|
88
|
+
3. **Terminal DOM Persistence**: xterm.js terminal instances retain their DOM structure across switches (prevents re-render flicker)
|
|
89
|
+
4. **Cache Efficiency**: Zustand stores persist open tabs, selections, and scroll positions per project
|
|
90
|
+
|
|
91
|
+
**Benefits:**
|
|
92
|
+
- Instant project switching (no DOM reconstruction)
|
|
93
|
+
- Terminal history preserved across switches
|
|
94
|
+
- Smooth UX without flashing/re-rendering
|
|
95
|
+
- Reduced network requests (cached UI state)
|
|
96
|
+
|
|
97
|
+
### Project Color, Avatar & Ordering (v2.0+)
|
|
98
|
+
**Storage**:
|
|
99
|
+
- Colors stored as optional `color` field in `Project` interface (hex string or undefined)
|
|
100
|
+
- Custom avatar images stored content-addressed at `~/.ppm/avatars/<sha256>.webp` via AvatarStorageService
|
|
101
|
+
|
|
102
|
+
**Endpoints:**
|
|
103
|
+
- `PATCH /api/projects/:name/color` — Update project color
|
|
104
|
+
- `POST /api/projects/:name/image` — Upload avatar (multipart, 2MB cap, client resizes to 128×128 webp 0.85)
|
|
105
|
+
- `GET /api/projects/:name/image` — Stream avatar (immutable cache headers, path-traversal safe)
|
|
106
|
+
- `DELETE /api/projects/:name/image` — Remove avatar (reverts to color+initials)
|
|
107
|
+
- `PATCH /api/projects/reorder` — Reorder projects array in config
|
|
108
|
+
|
|
109
|
+
**UI Components:**
|
|
110
|
+
- `ProjectBar` (52px sidebar) — Shows project avatars (custom image or color+initials), context menu for reorder/rename/delete/color-picker/change-image
|
|
111
|
+
- `ProjectBottomSheet` (mobile) — Bottom sheet switcher with long-press menu ("Change Image"/"Remove Image")
|
|
112
|
+
- `ProjectAvatar` component — Renders `<img>` with token auth + cache-bust query param, fallback to color+initials on missing/error
|
|
113
|
+
- `PROJECT_PALETTE` — 12-color palette for default colors when not customized
|
|
114
|
+
|
|
115
|
+
**Avatar Upload & Caching:**
|
|
116
|
+
- Client: canvas center-crop to 128×128, export as webp 0.85 quality (via `resize-image.ts`)
|
|
117
|
+
- Validation: MIME type (image/png, image/jpeg, image/webp, image/avif), file size ≤10MB
|
|
118
|
+
- Server: SHA256 hash for deduplication (same image across projects = same file)
|
|
119
|
+
- Cleanup: Deleted on project remove, preserved across rename (updated in project metadata)
|
|
120
|
+
- Auth: Token in URL query param allows `<img>` to load when auth enabled
|
|
121
|
+
- Cache: Immutable headers (max-age=31536000) + `?v=hash` cache-bust for updates
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Code Editor Migration (v2.0+)
|
|
126
|
+
|
|
127
|
+
**Migration**: CodeMirror 6 → Monaco Editor (@monaco-editor/react)
|
|
128
|
+
|
|
129
|
+
**Reasons:**
|
|
130
|
+
- Better syntax highlighting for complex languages
|
|
131
|
+
- Superior IntelliSense and code completion
|
|
132
|
+
- Performance improvements on large files
|
|
133
|
+
- More polished diff viewer experience
|
|
134
|
+
|
|
135
|
+
**Components Updated:**
|
|
136
|
+
- `src/web/components/editor/code-editor.tsx` — Monaco Editor with language detection
|
|
137
|
+
- `src/web/components/editor/diff-viewer.tsx` — Monaco diff viewer for git diffs
|
|
138
|
+
|
|
139
|
+
**Features:**
|
|
140
|
+
- Alt+Z toggle for word wrap
|
|
141
|
+
- Automatic language detection from file extension
|
|
142
|
+
- Theme sync with app dark/light mode
|
|
143
|
+
- Responsive layout with proper scrolling
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Terminal Flow
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
User clicks Terminal tab
|
|
151
|
+
↓
|
|
152
|
+
TerminalTab.tsx mounts
|
|
153
|
+
↓
|
|
154
|
+
useTerminal hook opens WebSocket: WS /ws/project/:name/terminal/:id
|
|
155
|
+
↓
|
|
156
|
+
TerminalService.spawn() creates PTY (Bun.spawn)
|
|
157
|
+
↓
|
|
158
|
+
xterm.js renders terminal emulator
|
|
159
|
+
↓
|
|
160
|
+
User types: "npm test"
|
|
161
|
+
↓
|
|
162
|
+
xterm.js captures key event
|
|
163
|
+
↓
|
|
164
|
+
Sends via WebSocket: { type: "input", data: "npm test\n" }
|
|
165
|
+
↓
|
|
166
|
+
TerminalService.write(pty, "npm test\n")
|
|
167
|
+
↓
|
|
168
|
+
npm process spawned inside PTY
|
|
169
|
+
↓
|
|
170
|
+
Output captured: "PASS: all tests\n"
|
|
171
|
+
↓
|
|
172
|
+
TerminalService sends: { type: "output", data: "PASS: all tests\n" }
|
|
173
|
+
↓
|
|
174
|
+
xterm.js renders output
|
|
175
|
+
↓
|
|
176
|
+
User resizes window → xterm.js resizes terminal
|
|
177
|
+
↓
|
|
178
|
+
Sends: { type: "resize", cols: 120, rows: 40 }
|
|
179
|
+
↓
|
|
180
|
+
TerminalService calls pty.resize()
|
|
181
|
+
↓
|
|
182
|
+
Shell (bash/zsh) receives SIGWINCH signal
|
|
183
|
+
↓
|
|
184
|
+
Terminal state updated
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Git Integration Flow
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
User right-clicks file in FileTree
|
|
193
|
+
↓
|
|
194
|
+
Context menu shows "Stage" option
|
|
195
|
+
↓
|
|
196
|
+
User clicks "Stage"
|
|
197
|
+
↓
|
|
198
|
+
FileActions.tsx calls POST /api/project/:name/git/stage
|
|
199
|
+
↓
|
|
200
|
+
Sends: { path: "src/index.ts" }
|
|
201
|
+
↓
|
|
202
|
+
GitService.stage(projectPath, "src/index.ts")
|
|
203
|
+
↓
|
|
204
|
+
Executes: git add src/index.ts (via simple-git)
|
|
205
|
+
↓
|
|
206
|
+
Returns: { ok: true }
|
|
207
|
+
↓
|
|
208
|
+
GitStatusPanel.tsx refreshes: GET /api/project/:name/git/status
|
|
209
|
+
↓
|
|
210
|
+
GitService.status() returns:
|
|
211
|
+
{
|
|
212
|
+
current: "main",
|
|
213
|
+
staged: ["src/index.ts"],
|
|
214
|
+
unstaged: ["README.md"],
|
|
215
|
+
untracked: ["temp.log"]
|
|
216
|
+
}
|
|
217
|
+
↓
|
|
218
|
+
UI updates: "src/index.ts" moves from "Unstaged" to "Staged"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Frontend Performance Optimization (v0.9.86+)
|
|
224
|
+
|
|
225
|
+
### Memory & Re-Render Reduction
|
|
226
|
+
|
|
227
|
+
**1. useShallow Pattern (Zustand)**
|
|
228
|
+
- All destructured store selectors wrapped in `useShallow()` (36 sites)
|
|
229
|
+
- Prevents unnecessary re-renders when object properties mutate
|
|
230
|
+
- Example: `const { messages, addMessage } = chatStore(useShallow(...))`
|
|
231
|
+
|
|
232
|
+
**2. Component Memoization (React.memo)**
|
|
233
|
+
- 10 heavy components wrapped (CodeEditor, MessageBubble, ProjectBar, ProjectAvatar, TerminalTab, PanelLayout, Sidebar, StatusBar, StatusBarEntry, TabBar, TreeNode)
|
|
234
|
+
- Memoization skips re-renders if props unchanged
|
|
235
|
+
- Paired with `useCallback` to maintain stable references
|
|
236
|
+
|
|
237
|
+
**3. Lazy Loading**
|
|
238
|
+
- MarkdownRenderer lazy-loaded from 3 sites (reduces initial bundle)
|
|
239
|
+
- CodeMirror on-demand in postgres-viewer
|
|
240
|
+
- Mermaid diagram support loaded dynamically only when diagram syntax detected
|
|
241
|
+
|
|
242
|
+
**4. Code Splitting (vite.config.ts)**
|
|
243
|
+
- 5 vendor chunks: `vendor-monaco`, `vendor-mermaid`, `vendor-xterm`, `vendor-markdown`, `vendor-ui`
|
|
244
|
+
- Heavy libraries (>500KB) in separate chunks for better browser caching
|
|
245
|
+
- Each chunk independently cacheable and updated
|
|
246
|
+
|
|
247
|
+
**5. Chat Pagination & Message Caps**
|
|
248
|
+
- Chat history loads 50 messages per page with load-more button (prevents DOM bloat)
|
|
249
|
+
- Team activity capped at 500 messages (prevents unbounded growth)
|
|
250
|
+
|
|
251
|
+
### Benefits
|
|
252
|
+
- Faster page load (lazy chunks load on-demand)
|
|
253
|
+
- Reduced re-render cycles (useShallow + memo)
|
|
254
|
+
- Lower memory footprint (capped message buffers)
|
|
255
|
+
- Better caching (vendor chunk stability across versions)
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## OS File Explorer Window
|
|
260
|
+
|
|
261
|
+
A floating, OS-skinned window (Windows 11 / macOS Finder chrome, Linux → macOS skin) that browses
|
|
262
|
+
the **whole host filesystem** — not just registered project directories — through the widened
|
|
263
|
+
`/api/fs` family. This is a deliberate scope change from every other file-facing route in PPM
|
|
264
|
+
(project-scoped, path-validated against one repo root): the explorer's authorization boundary is
|
|
265
|
+
"the whole disk, behind PPM's existing session auth", not "one project".
|
|
266
|
+
|
|
267
|
+
### FS scope = auth boundary
|
|
268
|
+
|
|
269
|
+
Every `/api/fs` route — including `docx-html`, `read`, `raw`, both SQLite doors — passes through
|
|
270
|
+
one shared guard chain (`src/services/fs-path-guard.service.ts`) before touching disk:
|
|
271
|
+
|
|
272
|
+
| Protection | Mechanism |
|
|
273
|
+
|---|---|
|
|
274
|
+
| PPM-dir shield | `assertNotPpmDir` refuses `getPpmDir()` (config, auth token, credentials) as a source **or** destination of any read or mutation, checked at both the given path and its resolved realpath (defeats a symlink pointed at the PPM dir) |
|
|
275
|
+
| Protected roots | `/`, drive roots (`C:\`), `$HOME` and the PPM dir itself refuse delete/rename/move as a source |
|
|
276
|
+
| Download tokens | `/api/fs/download/token` issues a single-use, path-bound token; `/api/fs/raw` spends it on first use, rejects replay and any path mismatch |
|
|
277
|
+
| Symlink safety | every op `lstat`s the entry itself (never follows to the target) so a link *to* a protected path can itself still be deleted, but nothing can read/write *through* one into the PPM dir |
|
|
278
|
+
| SQL injection surface | the external-DB doors (`/api/fs/sqlite/*`) block `ATTACH`/`DETACH` by keyword scan (after stripping comments/string literals) before executing any query — the same class of guard the project-scoped `/sqlite` route also needed |
|
|
279
|
+
| No event-loop blocking | every op is `fs.promises`-based with bounded concurrency and a per-entry timeout — a dead network mount or sleeping USB drive cannot stall unrelated requests, which matters once scope is the whole disk instead of one project |
|
|
280
|
+
|
|
281
|
+
### API surface
|
|
282
|
+
|
|
283
|
+
| Method | Path | Purpose |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| GET | `/api/system/host` | `HostInfo`: platform, path separator, homedir, drives, known folders, OS-pinned folders (Quick Access / Finder Favorites / GTK+KDE bookmarks), warnings |
|
|
286
|
+
| GET | `/api/fs/browse` | Directory listing (existing route, whitelist widened to `/`) |
|
|
287
|
+
| GET | `/api/fs/stat` | Single-entry metadata |
|
|
288
|
+
| POST | `/api/fs/copy` \| `/move` \| `/rename` \| `/touch` \| `/mkdir` | Mutations, collision (`EEXIST`)/self-nesting (`EINVAL`) reported for the client to resolve |
|
|
289
|
+
| DELETE | `/api/fs/delete` \| `/rmdir` | `{permanent?}` — OS trash (Recycle Bin / Trash / gio) by default, permanent on request |
|
|
290
|
+
| POST/GET | `/api/fs/download/token` / `/api/fs/raw` | Single-use, path-bound download |
|
|
291
|
+
| GET/POST | `/api/fs/sqlite/{tables,schema,data,query}` | External `.db` viewer — same shape as the project-scoped `/sqlite` route, `path` absolute, PPM dir refused |
|
|
292
|
+
|
|
293
|
+
`host-info.service.ts` orchestrates three OS-specific provider sets (`src/services/host-info/`)
|
|
294
|
+
behind a 60s cache with in-flight de-duplication (concurrent `?refresh=true` calls share one
|
|
295
|
+
rebuild rather than spawning N PowerShell/plutil/findmnt processes).
|
|
296
|
+
|
|
297
|
+
### Floating window layer
|
|
298
|
+
|
|
299
|
+
`src/web/components/floating-window/` — a content-agnostic window manager (zustand store):
|
|
300
|
+
drag/8-handle-resize gestures write geometry straight to the DOM element (no React re-render per
|
|
301
|
+
pointermove), committing to the store only on gesture end; rect + open windows persist to
|
|
302
|
+
`localStorage["ppm-windows"]`, restored once per app load and re-clamped to the current viewport.
|
|
303
|
+
|
|
304
|
+
Windows render in a portal at **z-30..38** (`30 + rank`, capped at an 8-window dense-rank limit) —
|
|
305
|
+
below the app's existing `z-40` click-away backdrops and `z-50` Radix layers, so command palette,
|
|
306
|
+
dropdowns and dialogs always stay reachable above any number of open explorer windows. Below the
|
|
307
|
+
`md` breakpoint the layer never mounts at all; `src/web/components/os-explorer/mobile/` renders the
|
|
308
|
+
same `ExplorerBody` component inside a full-screen bottom sheet instead (`variant="sheet"`).
|
|
309
|
+
Drag and resize share a `gestureAbandoned()` guard (`use-window-gesture-context.ts`) for the
|
|
310
|
+
pointer-up that still arrives after a mid-gesture window close (e.g. dragging by a titlebar button
|
|
311
|
+
that closes the frame).
|
|
312
|
+
|
|
313
|
+
**One chrome, every kind.** `WindowSkinChrome` (`window-skin-chrome.tsx`) is the titlebar every
|
|
314
|
+
window kind — explorer, team-member session, system monitor, detached tab — renders: it resolves
|
|
315
|
+
the active OS skin via `useExplorerSkin()` (Settings override, else host platform; Linux → macOS)
|
|
316
|
+
and delegates to that skin's `WindowsWindowChrome` / `MacosWindowChrome`
|
|
317
|
+
(`src/web/components/os-explorer/skins/`), scoped entirely through `[data-skin="windows"|"macos"]`
|
|
318
|
+
CSS variables layered over PPM's existing semantic theme tokens — no second color table. The
|
|
319
|
+
Windows skin's folder glyph draws only for `kind === "explorer"`; every other kind gets the bare
|
|
320
|
+
titlebar. The macOS skin boxes the title between the traffic lights and the PiP button as a flex
|
|
321
|
+
child (so a long title truncates instead of overlapping either), and puts the PiP button at the
|
|
322
|
+
titlebar's right end; the Windows skin puts it left of minimize.
|
|
323
|
+
|
|
324
|
+
**PiP is a capability of the frame, not of one kind.** `useWindowBodyElement`
|
|
325
|
+
(`use-window-body-element.ts`) creates the single DOM element `FloatingWindow` portals a window's
|
|
326
|
+
content into and publishes it as that window's PiP slot (`window-pip-registry.ts`, keyed by window
|
|
327
|
+
id). `PipCaptionButton` (`pip/pip-caption-button.tsx`), rendered by both skins, moves that slot into
|
|
328
|
+
a `documentPictureInPicture` window and back; it is absent (not disabled) where the API is
|
|
329
|
+
unsupported. `WindowPipPlaceholder` (`window-pip-placeholder.tsx`) takes the body's place in the
|
|
330
|
+
frame while it plays in PiP, with a ≥44px "Bring back" control. The mechanics below (attach/detach,
|
|
331
|
+
style mirroring, key forwarding, resize signalling) apply to whichever window kind currently owns
|
|
332
|
+
the slot — a tab-host window is only the one kind whose body is itself a portal target for another
|
|
333
|
+
component (`TabPool`).
|
|
334
|
+
|
|
335
|
+
#### Tab-host windows (detaching a tab into its own window)
|
|
336
|
+
|
|
337
|
+
- **Off-grid panel.** Detaching creates `` `__win__:${windowId}` `` (`windowPanelId()`,
|
|
338
|
+
`stores/panel-utils.ts`) — same treatment as `__dock__`: lives in `panels`, never in `grid`, so no
|
|
339
|
+
grid math (rows/columns/split) sees it. `stores/window-panel-actions.ts` is the only writer of
|
|
340
|
+
these panels and enforces the paired invariant: `focusedPanelId` never points at one (it would
|
|
341
|
+
send the next `openTab()` with no explicit panel into a window). `popOutTab`/`redockFromWindow`
|
|
342
|
+
create and destroy the panel and its window together; every close path (titlebar ×, keyboard,
|
|
343
|
+
reconcile) routes through `redockFromWindow`, which re-docks to the origin panel if it is still in
|
|
344
|
+
the grid, else the focused grid panel, else the first grid panel. All tab types pop out except
|
|
345
|
+
`system-monitor`, which already has its own window kind.
|
|
346
|
+
- **No-remount move.** `TabPool` (`components/layout/tab-pool.tsx`) mounts every tab once into a
|
|
347
|
+
wrapper `div[data-tab-pool-id]` created imperatively in `ReparentingTab`
|
|
348
|
+
(`components/layout/reparenting-tab.tsx`) and rendered into it via `createPortal` — React attaches
|
|
349
|
+
its listeners to the wrapper itself, so they keep firing after the node moves, including into
|
|
350
|
+
another document. A `useLayoutEffect` with no deps calls `appendChild` to move the wrapper into
|
|
351
|
+
whichever element last registered for the tab's panel id (`slotRegistry`,
|
|
352
|
+
`components/layout/tab-pool-registry.ts`); `TabHostWindowContent`'s slot `div` is always mounted
|
|
353
|
+
(never swapped for a placeholder), because it may currently be living inside the PiP document.
|
|
354
|
+
- **Persistence.** Window panels persist to their own global `localStorage["ppm-window-panels"]`
|
|
355
|
+
key (`stores/window-panel-persistence.ts`), separate from the per-project `ppm-panels-*` blob and
|
|
356
|
+
not synced to the server — the same limitation window geometry (`ppm-windows`) already has.
|
|
357
|
+
`WINDOW_KINDS` (`window-store-types.ts`) is the single list both the window store and
|
|
358
|
+
`window-persistence.ts` filter against; `team-member` is excluded from restore because its body
|
|
359
|
+
streams a live subagent session that cannot survive a reload.
|
|
360
|
+
- **Reconcile.** The two halves persist separately, so a reload can restore one without the other.
|
|
361
|
+
`reconcileTabHostWindows` (`stores/window-panel-reconcile.ts`), run once per project via
|
|
362
|
+
`useWindowPanelReconcile()` after the window layer restores, and unconditionally below `md` (the
|
|
363
|
+
window layer never mounts there): a panel whose window is gone comes back to the grid; a window
|
|
364
|
+
with no panel behind it closes.
|
|
365
|
+
- **PiP host.** `attachPipHost`/`isDocumentPipSupported` (`floating-window/pip/pip-host.ts`,
|
|
366
|
+
`pip-support.ts`) move a window's *slot* element — never a tab's own wrapper — into a
|
|
367
|
+
`documentPictureInPicture` window, one at a time per page. `pagehide` triggers a synchronous
|
|
368
|
+
restore (no `await` between it and the DOM move) so a closing PiP document never strips listeners
|
|
369
|
+
off a still-live terminal. `pip-style-copy.ts` mirrors stylesheets + `adoptedStyleSheets` once and
|
|
370
|
+
`<html>` class/inline theme CSS vars + `<body>` class **and inline style** on every theme change
|
|
371
|
+
(the page background is an inline `background: var(--bg)` on `<body>` in `index.html`, not a
|
|
372
|
+
class), plus a MutationObserver for Vite HMR-injected `<style>` tags. `pip-key-forward.ts`
|
|
373
|
+
re-dispatches keydown/keyup from the PiP window onto the main window for app-level shortcuts,
|
|
374
|
+
skipping targets that own their own input (`input`, `.monaco-editor`, `.xterm-helper-textarea`,
|
|
375
|
+
etc.). `pip-resize-signal.ts` dispatches a non-bubbling `ppm:host-resize` CustomEvent on each
|
|
376
|
+
`[data-tab-pool-id]` wrapper inside the slot (on attach, every PiP `resize`, and on detach); the
|
|
377
|
+
terminal and editors subscribe via `onHostResize` and re-fit, because main-window
|
|
378
|
+
`ResizeObserver`s are late or silent for a PiP-driven size.
|
|
379
|
+
- **Radix portals in PiP.** `PortalContainerProvider` (`components/ui/portal-container-context.tsx`)
|
|
380
|
+
is mounted inside `ReparentingTab` around each tab's content, fed the PiP document's `body` while
|
|
381
|
+
that tab's window is in PiP (`usePipPortalContainer`, `window-pip-registry.ts`) — so a tab's own
|
|
382
|
+
dropdowns/tooltips/dialogs render inside the PiP document instead of opening unreachably in the
|
|
383
|
+
main window. `undefined` (document default) while docked.
|
|
384
|
+
- **Known limitations** (see the comment block atop `pip/pip-caption-button.tsx`): sonner toasts
|
|
385
|
+
always render in the main window (one app-root toaster); `useIsMobile()`/Tailwind `md:` read the
|
|
386
|
+
main window's viewport, not the PiP window's; `onSelect`/`selectionchange` degrades for PiP
|
|
387
|
+
content; the terminal's reconnect check reads the main document's visibility; Monaco keybindings
|
|
388
|
+
(Ctrl+Z, Ctrl+F, …) don't fire while the editor sits in PiP (typing still reaches the buffer); a
|
|
389
|
+
tab-host window's titlebar keeps the tab title captured at pop-out time.
|
|
390
|
+
- **Mobile.** Pop-out and PiP are hidden entirely below `md` (`useIsMobile()`) — never a scaled-down
|
|
391
|
+
window.
|
|
@@ -690,7 +690,7 @@ Simplified interface: `createSession()` → `send()` / `stream()`.
|
|
|
690
690
|
```typescript
|
|
691
691
|
import { unstable_v2_createSession } from "@anthropic-ai/claude-agent-sdk";
|
|
692
692
|
|
|
693
|
-
await using session = unstable_v2_createSession({ model: "claude-opus-
|
|
693
|
+
await using session = unstable_v2_createSession({ model: "claude-opus-5" });
|
|
694
694
|
|
|
695
695
|
await session.send("Hello!");
|
|
696
696
|
for await (const msg of session.stream()) {
|