@hienlh/ppm 0.18.2 → 0.18.4
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/CHANGELOG.md +22 -0
- package/CLAUDE.md +1 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +6 -1
- package/dist/web/assets/{ai-resource-editor-B6CODu6u.js → ai-resource-editor-Bb-NNlp1.js} +1 -1
- package/dist/web/assets/{angular-html-C-Kjr43S.js → angular-html-jfMqUuRd.js} +1 -1
- package/dist/web/assets/{angular-ts-CaDDbZqm.js → angular-ts-wytn9PMm.js} +1 -1
- package/dist/web/assets/api-client-CzABTreM.js +1 -0
- package/dist/web/assets/{api-settings-CAhcNpj5.js → api-settings-DfNRPr1q.js} +1 -1
- package/dist/web/assets/api-settings-DlcoR5QT.js +1 -0
- package/dist/web/assets/{api-themes-CAjg_Bt9.js → api-themes-CEyYK7AK.js} +1 -1
- package/dist/web/assets/{apl-B1WsQFAb.js → apl-i-ahKx6O.js} +1 -1
- package/dist/web/assets/architecture-PBZL5I3N-Bs7wzj-W.js +1 -0
- package/dist/web/assets/arrow-right-DubGFkN8.js +1 -0
- package/dist/web/assets/{astro-Bdnz7nVp.js → astro-DVBCJfG6.js} +1 -1
- package/dist/web/assets/audio-preview-DG2Tw0cO.js +1 -0
- package/dist/web/assets/{blade-B2euh8sS.js → blade-BHlL8SqU.js} +1 -1
- package/dist/web/assets/{bsl-GNeMaxd0.js → bsl-D-UOfR1F.js} +1 -1
- package/dist/web/assets/button-B4MWPjWj.js +1 -0
- package/dist/web/assets/{cairo-D7G0W7-s.js → cairo-Bn6rucli.js} +1 -1
- package/dist/web/assets/chat-tab-CgAv-crh.js +15 -0
- package/dist/web/assets/{cobol-Q6YgysnI.js → cobol-Byn9FYKu.js} +1 -1
- package/dist/web/assets/code-editor-bX4jlgMy.js +10 -0
- package/dist/web/assets/{coffee-BjufWxVc.js → coffee-DjBl4gm-.js} +1 -1
- package/dist/web/assets/{conflict-editor-DnSltcyC.js → conflict-editor-B6hVffX3.js} +3 -3
- package/dist/web/assets/{context-menu-q-RHHAWz.js → context-menu-DkQdt6YA.js} +1 -1
- package/dist/web/assets/{cpp-CqWU0gCR.js → cpp-CKzic-3E.js} +1 -1
- package/dist/web/assets/{crystal-ylayVajF.js → crystal-Bkg-Ly_g.js} +1 -1
- package/dist/web/assets/{csv-preview-DnaFdgOB.js → csv-preview-hL4Z9HS5.js} +1 -1
- package/dist/web/assets/{data-grid-overlay-editor-BzPfsCfd.js → data-grid-overlay-editor-B1mTl3KS.js} +1 -1
- package/dist/web/assets/{database-viewer-C7uLy8Z1.js → database-viewer-CTK5kMaA.js} +1 -1
- package/dist/web/assets/diff-viewer-DZxnANY4.js +4 -0
- package/dist/web/assets/{docx-preview-Dd2nTW2v.js → docx-preview-CrAc7Qjv.js} +1 -1
- package/dist/web/assets/download-D9UYwY0-.js +1 -0
- package/dist/web/assets/{edge-DX4NxQfh.js → edge-DVbZnd3Y.js} +1 -1
- package/dist/web/assets/{edit-diff-preview-DfX-9om9.js → edit-diff-preview-HTZPC9VP.js} +1 -1
- package/dist/web/assets/{elixir-CSh3NjbV.js → elixir-Dwi8Wu7W.js} +1 -1
- package/dist/web/assets/{elm-DBzwFYq9.js → elm-DtIv-HXJ.js} +1 -1
- package/dist/web/assets/{erb-yxVOb8aB.js → erb-D3HEDz1x.js} +1 -1
- package/dist/web/assets/{erlang-5XIDkYrM.js → erlang-2MjTCzGz.js} +1 -1
- package/dist/web/assets/{esm-DO4el2OJ.js → esm-CZ8B8Ba2.js} +1 -1
- package/dist/web/assets/explorer-body-DykJXCTw.js +2 -0
- package/dist/web/assets/explorer-window-content-NS8Y0v8c.js +1 -0
- package/dist/web/assets/{extension-webview-Dk_zyoXZ.js → extension-webview-D6eywKPN.js} +1 -1
- package/dist/web/assets/{fortran-fixed-form-D4tE5VWY.js → fortran-fixed-form-BLyAtyPM.js} +1 -1
- package/dist/web/assets/{fsharp-By8soj6-.js → fsharp-Bxbn_gbr.js} +1 -1
- package/dist/web/assets/{gdresource-DoSwM7Hv.js → gdresource-sFMY4D2Q.js} +1 -1
- package/dist/web/assets/{git-commit-_ZiciHIx.js → git-commit-BzB9Lio5.js} +1 -1
- package/dist/web/assets/{git-log-panel-BLQSy6rC.js → git-log-panel-DAogZ9QV.js} +1 -1
- package/dist/web/assets/{git-rebase-C3Y5N9-s.js → git-rebase-CKSVd848.js} +1 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-D7i54kbM.js +1 -0
- package/dist/web/assets/glide-data-grid-DxfJRN-r.js +138 -0
- package/dist/web/assets/{glimmer-js-BPBTXVlC.js → glimmer-js-BY8aExAJ.js} +1 -1
- package/dist/web/assets/{glimmer-ts-BQSZdy_D.js → glimmer-ts-Bak54dBX.js} +1 -1
- package/dist/web/assets/{glsl-CbQdxpU-.js → glsl-nR2eaJQe.js} +1 -1
- package/dist/web/assets/{graphql-GMlKikpY.js → graphql-CGPPFAU6.js} +1 -1
- package/dist/web/assets/group-chat-tab-DkVi6qL2.js +1 -0
- package/dist/web/assets/{hack-DyshcLec.js → hack-CXIMX-Ol.js} +1 -1
- package/dist/web/assets/{haml-rqCqWzsq.js → haml-tPY-9Pfm.js} +1 -1
- package/dist/web/assets/{handlebars-BCKwSWjR.js → handlebars-BuW1LT7T.js} +1 -1
- package/dist/web/assets/{html-Cg29xPR1.js → html-bgVcwZ-t.js} +1 -1
- package/dist/web/assets/{html-derivative-geltIVg8.js → html-derivative-D6-IxTgn.js} +1 -1
- package/dist/web/assets/{http-pG7Vp237.js → http-CJ1zyB-h.js} +1 -1
- package/dist/web/assets/{hurl-Di3Uu_Mc.js → hurl-CmoT7l3T.js} +1 -1
- package/dist/web/assets/{hxml-CRy4lbv8.js → hxml-CztOsL8o.js} +1 -1
- package/dist/web/assets/{image-preview-KubLSLNO.js → image-preview-Byjx8MoL.js} +1 -1
- package/dist/web/assets/index-Bucbpby-.css +2 -0
- package/dist/web/assets/index-CcN0TBm4.js +52 -0
- package/dist/web/assets/info-3K5VOQVL-BV41AzR9.js +1 -0
- package/dist/web/assets/{jinja-C8GyVnvd.js → jinja-B1pq3jJq.js} +1 -1
- package/dist/web/assets/{jison-BywnqPn6.js → jison-D8B5pCwA.js} +1 -1
- package/dist/web/assets/{julia-DTrO3T3B.js → julia-D0K3bwpU.js} +1 -1
- package/dist/web/assets/{just-CA3KSHPi.js → just-B9Jlddu-.js} +1 -1
- package/dist/web/assets/keybindings-store-DJIHqbLM.js +1 -0
- package/dist/web/assets/{latex-BC5xUMpc.js → latex-BPJSSE6j.js} +1 -1
- package/dist/web/assets/{liquid-Chd8tSyQ.js → liquid-lv10fo4U.js} +1 -1
- package/dist/web/assets/{lua-DHaEtwLv.js → lua-PNuaIBEF.js} +1 -1
- package/dist/web/assets/{markdown-renderer-B4uL6GVA.js → markdown-renderer-CXlEe3ma.js} +3 -3
- package/dist/web/assets/markdown-renderer-pYznJDbh.js +1 -0
- package/dist/web/assets/{marko-Desx_bC-.js → marko-Du3frD6X.js} +1 -1
- package/dist/web/assets/{mdc-DahWHc1t.js → mdc-BeDVcFRl.js} +1 -1
- package/dist/web/assets/media-url-C5vA0ha9.js +1 -0
- package/dist/web/assets/{mobile-bottom-sheet-BQt45q9T.js → mobile-bottom-sheet-CS3XMwFw.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-BuqBlP3o.js +1 -0
- package/dist/web/assets/{nextflow-3A7lutCA.js → nextflow-DkcXP71s.js} +1 -1
- package/dist/web/assets/{nginx-XQda6Hdy.js → nginx-C5n8N_bt.js} +1 -1
- package/dist/web/assets/{nim-DHqlpwP1.js → nim-CIhyfs-A.js} +1 -1
- package/dist/web/assets/notification-store-Bcl5efY2.js +1 -0
- package/dist/web/assets/{number-overlay-editor-DgZtViPO.js → number-overlay-editor-NgOsWJ7x.js} +1 -1
- package/dist/web/assets/packet-RMMSAZCW-CaJoP44m.js +1 -0
- package/dist/web/assets/{panel-store-s6Qx49RK.js → panel-store-DCD82zc1.js} +1 -1
- package/dist/web/assets/{pdf-preview-xZQVUtaV.js → pdf-preview-F2DWVB85.js} +1 -1
- package/dist/web/assets/{perl-M-ncjACb.js → perl-Cf31sZYE.js} +1 -1
- package/dist/web/assets/{php-CKIajrZi.js → php-Dzl_MDIW.js} +1 -1
- package/dist/web/assets/pie-UPGHQEXC-DSGZmNw2.js +1 -0
- package/dist/web/assets/plus-DmjC2iRB.js +1 -0
- package/dist/web/assets/{postgres-viewer-Ds0FxBel.js → postgres-viewer-KZqPYfOB.js} +3 -3
- package/dist/web/assets/{project-store-DyaEpHVG.js → project-store-B8BkHXy5.js} +1 -1
- package/dist/web/assets/{pug-DO0u_gO4.js → pug-CxSYHrB3.js} +1 -1
- package/dist/web/assets/{qml-UEje1PAA.js → qml-DlEjGhwN.js} +1 -1
- package/dist/web/assets/radar-KQ55EAFF-Be4VUTs0.js +1 -0
- package/dist/web/assets/{razor-lCs5T1Ib.js → razor-KVt5uW14.js} +1 -1
- package/dist/web/assets/{rst-CZX_O0z4.js → rst-D5BwNTnw.js} +1 -1
- package/dist/web/assets/{ruby-idycVAyE.js → ruby-D5fejQwd.js} +1 -1
- package/dist/web/assets/run-in-terminal-B8TDtaM_.js +1 -0
- package/dist/web/assets/{sas-o5gTfUwp.js → sas-Bj7B1CB2.js} +1 -1
- package/dist/web/assets/{scss-CRTk3iBQ.js → scss-BB0CUoCL.js} +1 -1
- package/dist/web/assets/send-to-chat-CuBAVC8l.js +1 -0
- package/dist/web/assets/{settings-store-ChqHdT2V.js → settings-store-CY-4_U3H.js} +2 -2
- package/dist/web/assets/settings-tab-Khj-w6Sr.js +1 -0
- package/dist/web/assets/{shaderlab-3e5ln8ci.js → shaderlab-BdjEZUMc.js} +1 -1
- package/dist/web/assets/{shellsession-CGQjQ75I.js → shellsession-Bp1OA1rR.js} +1 -1
- package/dist/web/assets/{soy-CxY0VwX2.js → soy-DlYz3wsC.js} +1 -1
- package/dist/web/assets/{sparql-CBAGAFuq.js → sparql-CdfII5Jd.js} +1 -1
- package/dist/web/assets/sql-query-editor-Cp0_e2P1.js +1 -0
- package/dist/web/assets/{sqlite-viewer-CZYDknNa.js → sqlite-viewer-CIv3sagZ.js} +1 -1
- package/dist/web/assets/{stata-BoezhOvk.js → stata-BvUA6GUW.js} +1 -1
- package/dist/web/assets/{surrealql-CHEzAhu7.js → surrealql--DLNbTyI.js} +1 -1
- package/dist/web/assets/{svelte-y6xtu2zX.js → svelte-CBfSsG4n.js} +1 -1
- package/dist/web/assets/system-monitor-tab-CrssTOf4.js +1 -0
- package/dist/web/assets/{tab-store-C42sktPo.js → tab-store-DAsvpx4t.js} +1 -1
- package/dist/web/assets/team-member-window-content-D-hXax3c.js +1 -0
- package/dist/web/assets/{templ-DbG2y5yD.js → templ-Dv8zpKZW.js} +1 -1
- package/dist/web/assets/terminal-tab-BIQDG-Ti.js +3 -0
- package/dist/web/assets/{tex-CeKQhrQG.js → tex-CsrOiqh1.js} +1 -1
- package/dist/web/assets/tool-cards-Dx-Rrpri.js +8 -0
- package/dist/web/assets/treemap-KZPCXAKY-Cu2zgbxU.js +1 -0
- package/dist/web/assets/{ts-tags-iKKOXJjG.js → ts-tags-3T98k4EJ.js} +1 -1
- package/dist/web/assets/{twig-DK8Vrp-G.js → twig-u6QZ45FH.js} +1 -1
- package/dist/web/assets/{use-blob-url-DYtOHK7u.js → use-blob-url-BmlYYZ8L.js} +1 -1
- package/dist/web/assets/use-is-mobile-B8FA4kQf.js +1 -0
- package/dist/web/assets/use-monaco-theme-Djp1_fKi.js +1 -0
- package/dist/web/assets/use-websocket-CCYA9-a2.js +1 -0
- package/dist/web/assets/{vendor-mermaid-C977xNeE.js → vendor-mermaid-Bg4vE8QA.js} +1 -1
- package/dist/web/assets/video-preview-cvjNMjWA.js +1 -0
- package/dist/web/assets/{vue-JkIEObMV.js → vue-CxIsxtwn.js} +1 -1
- package/dist/web/assets/{vue-html-D8SqnU86.js → vue-html-BGL5qnuH.js} +1 -1
- package/dist/web/assets/{vue-vine-DJj7N4WD.js → vue-vine-DHPh_gMr.js} +1 -1
- package/dist/web/assets/{xml-C-00w9Rv.js → xml-C-vM2m7J.js} +1 -1
- package/dist/web/assets/{xsl-CVdCwPHV.js → xsl-DvTXNwuO.js} +1 -1
- package/dist/web/index.html +21 -20
- package/dist/web/sw.js +1 -1
- package/docs/codebase-summary.md +1 -0
- package/package.json +1 -1
- package/scripts/dev-quick-tunnel-retry.mjs +51 -0
- package/src/providers/claude-agent-sdk.ts +6 -0
- package/src/server/helpers/media-route-handlers.ts +66 -0
- package/src/server/helpers/range-file-response.ts +86 -0
- package/src/server/index.ts +24 -17
- package/src/server/middleware/auth.ts +3 -2
- package/src/server/routes/files.ts +40 -7
- package/src/server/routes/fs-browse.ts +43 -5
- package/src/server/routes/fs-upload.ts +32 -0
- package/src/server/routes/teams.ts +34 -0
- package/src/server/ws/chat.ts +64 -16
- package/src/server/ws/team-inbox-watcher.ts +61 -22
- package/src/server/ws/terminal.ts +24 -6
- package/src/services/fs-ops/fs-ops-upload.service.ts +120 -0
- package/src/services/media-transcode/ffmpeg-capabilities.ts +89 -0
- package/src/services/media-transcode/media-probe.ts +63 -0
- package/src/services/media-transcode/transcode-stream.ts +129 -0
- package/src/services/subagent-transcript-merger.ts +1 -1
- package/src/services/team-member-activity/member-activity.service.ts +117 -0
- package/src/services/team-member-activity/outbound-message-scanner.ts +121 -0
- package/src/services/team-member-activity/subagent-transcript-index.ts +95 -0
- package/src/services/team-member-activity/transcript-tail-summary.ts +125 -0
- package/src/shared/background-agent-status.ts +79 -0
- package/src/types/api.ts +1 -0
- package/src/types/chat.ts +6 -1
- package/src/web/components/chat/chat-history-bar.tsx +4 -2
- package/src/web/components/chat/chat-tab.tsx +1 -0
- package/src/web/components/chat/message-input.tsx +13 -6
- package/src/web/components/chat/mini-markdown.tsx +17 -0
- package/src/web/components/chat/send-message-card.tsx +134 -0
- package/src/web/components/chat/send-message-parse.ts +159 -0
- package/src/web/components/chat/team-activity-panel.tsx +107 -121
- package/src/web/components/chat/team-member-activity-row.tsx +92 -0
- package/src/web/components/chat/team-member-list.tsx +43 -0
- package/src/web/components/chat/team-member-window-content.tsx +97 -0
- package/src/web/components/chat/team-message-badges.ts +18 -0
- package/src/web/components/chat/team-message-list.tsx +88 -0
- package/src/web/components/chat/team-message-preview.ts +87 -0
- package/src/web/components/chat/tool-cards.tsx +100 -23
- package/src/web/components/editor/audio-preview.tsx +6 -7
- package/src/web/components/editor/video-player/use-video-keyboard-shortcuts.ts +62 -0
- package/src/web/components/editor/video-player/video-player-controls.tsx +103 -0
- package/src/web/components/editor/video-player/video-player.tsx +190 -0
- package/src/web/components/editor/video-player/video-settings-menu.tsx +92 -0
- package/src/web/components/editor/video-player/video-transform.ts +49 -0
- package/src/web/components/editor/video-preview.tsx +75 -14
- package/src/web/components/explorer/file-tree.tsx +6 -0
- package/src/web/components/explorer/tree-node-context-menu.tsx +5 -1
- package/src/web/components/floating-window/window-content-registry.ts +5 -0
- package/src/web/components/floating-window/window-store-types.ts +1 -1
- package/src/web/components/os-explorer/actions/explorer-actions-upload.ts +96 -0
- package/src/web/components/os-explorer/actions/use-explorer-actions.ts +23 -2
- package/src/web/components/os-explorer/actions/use-explorer-upload-actions.tsx +41 -0
- package/src/web/components/os-explorer/dnd/use-drop-transfer.tsx +10 -1
- package/src/web/components/os-explorer/dnd/use-entry-row-dnd.ts +8 -1
- package/src/web/components/os-explorer/dnd/use-external-file-drop.ts +84 -0
- package/src/web/components/os-explorer/dnd/use-path-drop-target.ts +46 -3
- package/src/web/components/os-explorer/explorer-body.tsx +1 -1
- package/src/web/components/os-explorer/explorer-context-menu.tsx +10 -0
- package/src/web/components/os-explorer/explorer-dialogs.tsx +2 -0
- package/src/web/components/os-explorer/explorer-sidebar.tsx +10 -5
- package/src/web/components/os-explorer/explorer-toolbar.tsx +32 -2
- package/src/web/components/os-explorer/mobile/mobile-bottom-toolbar.tsx +5 -1
- package/src/web/components/os-explorer/upload/collect-dropped-entries.ts +81 -0
- package/src/web/components/os-explorer/upload/upload-queue.ts +79 -0
- package/src/web/components/os-explorer/upload/use-upload-picker.tsx +74 -0
- package/src/web/components/os-explorer/views/column-row.tsx +3 -1
- package/src/web/components/os-explorer/views/column-view-column.tsx +6 -1
- package/src/web/components/os-explorer/views/icons-view-tile.tsx +1 -1
- package/src/web/components/os-explorer/views/icons-view.tsx +5 -1
- package/src/web/components/os-explorer/views/list-row.tsx +1 -1
- package/src/web/components/os-explorer/views/list-view.tsx +5 -1
- package/src/web/components/shared/markdown-code-block.tsx +3 -7
- package/src/web/components/terminal/terminal-tab.tsx +19 -25
- package/src/web/hooks/use-chat.ts +77 -15
- package/src/web/hooks/use-team-activity-feed.ts +92 -0
- package/src/web/hooks/use-terminal-command-queue.ts +108 -0
- package/src/web/hooks/use-terminal.ts +45 -3
- package/src/web/lib/fs-api.ts +9 -0
- package/src/web/lib/fs-upload-xhr.ts +62 -0
- package/src/web/lib/media-url.ts +51 -0
- package/src/web/lib/open-terminal-at.ts +22 -0
- package/src/web/lib/run-in-terminal.ts +55 -0
- package/src/web/lib/send-to-chat.ts +118 -0
- package/src/web/lib/ws-auth.ts +13 -0
- package/src/web/lib/ws-client.ts +10 -5
- package/.release-run.sh +0 -5
- package/dist/web/assets/api-client-CaRE4HYh.js +0 -1
- package/dist/web/assets/api-settings-DBcXfeJ8.js +0 -1
- package/dist/web/assets/architecture-PBZL5I3N-B4J2-O8Q.js +0 -1
- package/dist/web/assets/audio-preview-BkNAEqcB.js +0 -1
- package/dist/web/assets/chat-tab-B-MEsHYp.js +0 -13
- package/dist/web/assets/code-editor-bC1KAEUA.js +0 -10
- package/dist/web/assets/dialog-CkI7rwrh.js +0 -1
- package/dist/web/assets/diff-viewer-BwElk4q9.js +0 -4
- package/dist/web/assets/explorer-body-DzdXBlWw.js +0 -2
- package/dist/web/assets/explorer-window-content-Dgl1WTNs.js +0 -1
- package/dist/web/assets/gitGraph-HDMCJU4V-CtQ3yXN-.js +0 -1
- package/dist/web/assets/glide-data-grid-QZ_fgjIa.js +0 -138
- package/dist/web/assets/group-chat-tab-BbdVlKFv.js +0 -1
- package/dist/web/assets/index-D-xwjNyX.css +0 -2
- package/dist/web/assets/index-wd3FSHHn.js +0 -52
- package/dist/web/assets/info-3K5VOQVL-Cg0mIJn9.js +0 -1
- package/dist/web/assets/keybindings-store-Bp_c_C3z.js +0 -1
- package/dist/web/assets/markdown-renderer-2asl-3Kk.js +0 -1
- package/dist/web/assets/mobile-explorer-sheet-LWUDaNBV.js +0 -1
- package/dist/web/assets/notification-store-BasUnpt7.js +0 -1
- package/dist/web/assets/packet-RMMSAZCW-D79vAFR_.js +0 -1
- package/dist/web/assets/pie-UPGHQEXC-DYUOH68U.js +0 -1
- package/dist/web/assets/plus-9LatwJMx.js +0 -1
- package/dist/web/assets/radar-KQ55EAFF-B1eznNtd.js +0 -1
- package/dist/web/assets/settings-tab-vhatQ51-.js +0 -1
- package/dist/web/assets/sql-query-editor-BblB2Imt.js +0 -1
- package/dist/web/assets/system-monitor-tab-D8dy-RT_.js +0 -1
- package/dist/web/assets/terminal-tab-CfD22q5x.js +0 -3
- package/dist/web/assets/tool-cards-DNgw1yI6.js +0 -7
- package/dist/web/assets/treemap-KZPCXAKY-SeI2TxLs.js +0 -1
- package/dist/web/assets/use-is-mobile-DnFSkUG2.js +0 -1
- package/dist/web/assets/use-monaco-theme-DEbn-QT3.js +0 -1
- package/dist/web/assets/video-preview-DcUaFYyv.js +0 -1
- package/src/web/components/chat/team-activity-popover.tsx +0 -202
- /package/dist/web/assets/{abap-DrMyiWmK.js → abap-C1K0-wzc.js} +0 -0
- /package/dist/web/assets/{actionscript-3-BlSOyi0y.js → actionscript-3-CJhQVjLA.js} +0 -0
- /package/dist/web/assets/{ada-B_m96icB.js → ada-Bt8r9fME.js} +0 -0
- /package/dist/web/assets/{andromeeda-Ch3vOiZB.js → andromeeda-DF1I8qi3.js} +0 -0
- /package/dist/web/assets/{apache-CdsaXc1n.js → apache-CnlleE6P.js} +0 -0
- /package/dist/web/assets/{apex-0AnsBevk.js → apex-j0C6Xmk6.js} +0 -0
- /package/dist/web/assets/{api-client-rTAGdEKo.js → api-client-BSB14J4w.js} +0 -0
- /package/dist/web/assets/{applescript-CSxLolzy.js → applescript-DO7AnO8U.js} +0 -0
- /package/dist/web/assets/{apply-theme-CMQXnUAa.js → apply-theme-CNj1iA-v.js} +0 -0
- /package/dist/web/assets/{ara-DXZS0jA5.js → ara-i83AydZj.js} +0 -0
- /package/dist/web/assets/{asciidoc-DDy3pH2A.js → asciidoc-DbBA6-sF.js} +0 -0
- /package/dist/web/assets/{asm-Bd17hhqg.js → asm-B5S47Rgr.js} +0 -0
- /package/dist/web/assets/{aurora-x-C62HJRs-.js → aurora-x-De_IaSKw.js} +0 -0
- /package/dist/web/assets/{awk-Cc-nazni.js → awk-CfyLHXqS.js} +0 -0
- /package/dist/web/assets/{ayu-dark-BsfmNWCU.js → ayu-dark-DTLdZ1Si.js} +0 -0
- /package/dist/web/assets/{ayu-light-BZ4HLa4X.js → ayu-light-DTOfkzhu.js} +0 -0
- /package/dist/web/assets/{ayu-mirage-Bhyjyiqr.js → ayu-mirage-DsAECCMv.js} +0 -0
- /package/dist/web/assets/{ballerina-QiWgir3v.js → ballerina-CumGp9A7.js} +0 -0
- /package/dist/web/assets/{bat-DxtlkwN3.js → bat-Ca2SBbYN.js} +0 -0
- /package/dist/web/assets/{beancount-Bt21X7M_.js → beancount-CqyXBwwF.js} +0 -0
- /package/dist/web/assets/{berry-Cksre7lO.js → berry-yl-kJr_H.js} +0 -0
- /package/dist/web/assets/{bibtex-DkakgDlR.js → bibtex-DoFFszjV.js} +0 -0
- /package/dist/web/assets/{bicep-8crmcyXU.js → bicep-CWPi4nfK.js} +0 -0
- /package/dist/web/assets/{bird2-CewQ2I_Z.js → bird2-DXew-ut0.js} +0 -0
- /package/dist/web/assets/{bot-nk1k3ELu.js → bot-CunPuEdW.js} +0 -0
- /package/dist/web/assets/{c-C_6zE8Ha.js → c-clbySDvd.js} +0 -0
- /package/dist/web/assets/{c3-DLWB9-dp.js → c3-B65jvSM1.js} +0 -0
- /package/dist/web/assets/{cadence-D8qYS6pR.js → cadence-Cm8tQTHN.js} +0 -0
- /package/dist/web/assets/{can-open-in-ppm-BzVXoV-d.js → can-open-in-ppm-Cu-XYIgu.js} +0 -0
- /package/dist/web/assets/{catppuccin-frappe-Bh95lNRy.js → catppuccin-frappe-DLeGslh_.js} +0 -0
- /package/dist/web/assets/{catppuccin-latte-C1PlN3G0.js → catppuccin-latte-CR5GeHws.js} +0 -0
- /package/dist/web/assets/{catppuccin-macchiato-CldtgdK4.js → catppuccin-macchiato-p-XgjxhP.js} +0 -0
- /package/dist/web/assets/{catppuccin-mocha-BiwqxX9A.js → catppuccin-mocha-B8QOAZLl.js} +0 -0
- /package/dist/web/assets/{clarity-CGiU22jY.js → clarity-DPulbvKH.js} +0 -0
- /package/dist/web/assets/{clojure-COIC3doh.js → clojure-DFuWS20u.js} +0 -0
- /package/dist/web/assets/{cmake-DWUEQfHv.js → cmake-DejCqcu4.js} +0 -0
- /package/dist/web/assets/{codeowners-BNHiAQea.js → codeowners-BwAyqWIH.js} +0 -0
- /package/dist/web/assets/{codeql-BuKaoS0j.js → codeql-DgSwFjqI.js} +0 -0
- /package/dist/web/assets/{common-lisp-D5IKbxsd.js → common-lisp-CxIVJCtf.js} +0 -0
- /package/dist/web/assets/{coq-BMVS-p94.js → coq-DLSjSnmf.js} +0 -0
- /package/dist/web/assets/{csharp-DMlbXbM4.js → csharp-C1HHzXBp.js} +0 -0
- /package/dist/web/assets/{css-CjkJC89X.js → css-VXsMY--y.js} +0 -0
- /package/dist/web/assets/{csv-DoNVBBf0.js → csv-BEbcJdHz.js} +0 -0
- /package/dist/web/assets/{csv-parser-DHvoTjMF.js → csv-parser-r-Nk1oHT.js} +0 -0
- /package/dist/web/assets/{cue-DoTRbBg-.js → cue-BkDnFqUD.js} +0 -0
- /package/dist/web/assets/{cypher-DVsNdTT3.js → cypher-1cMqojSQ.js} +0 -0
- /package/dist/web/assets/{d-EX7BBSrv.js → d-BnYjRXtH.js} +0 -0
- /package/dist/web/assets/{dark-plus-P_YTHwlh.js → dark-plus-XfbaH7kZ.js} +0 -0
- /package/dist/web/assets/{dart-Do-rgU4S.js → dart-Ca7s-8hK.js} +0 -0
- /package/dist/web/assets/{data-grid-types-CUx8cusN.js → data-grid-types-BXwlFMzs.js} +0 -0
- /package/dist/web/assets/{dax-DxOieacu.js → dax-CmuOw-mY.js} +0 -0
- /package/dist/web/assets/{desktop-CvdV7SBf.js → desktop-C2gJ6Ovb.js} +0 -0
- /package/dist/web/assets/{diff-CNOetGen.js → diff-BCEo1Ej7.js} +0 -0
- /package/dist/web/assets/{dist-CMfLpPW_.js → dist-CUPbqNnL.js} +0 -0
- /package/dist/web/assets/{dist-DduBIVkD.js → dist-DOlBnxFX.js} +0 -0
- /package/dist/web/assets/{docker-DAlG4_q-.js → docker-DN2jlyAh.js} +0 -0
- /package/dist/web/assets/{dotenv-Dh3qf6yx.js → dotenv-NExMY2kS.js} +0 -0
- /package/dist/web/assets/{dracula-baAGmJYG.js → dracula-B9lbE01y.js} +0 -0
- /package/dist/web/assets/{dracula-soft-l7ZIyums.js → dracula-soft-CMBBfKF_.js} +0 -0
- /package/dist/web/assets/{dream-maker-DeRjdBn8.js → dream-maker-DEyDwFsd.js} +0 -0
- /package/dist/web/assets/{emacs-lisp-CB05-jQA.js → emacs-lisp-D_qP2zS6.js} +0 -0
- /package/dist/web/assets/{everforest-dark-CT-llQBT.js → everforest-dark-DbHa2U-p.js} +0 -0
- /package/dist/web/assets/{everforest-light-ByPJkTT7.js → everforest-light-BISkJchP.js} +0 -0
- /package/dist/web/assets/{fennel-CeMarbvn.js → fennel-6B22SfJR.js} +0 -0
- /package/dist/web/assets/{file-exclamation-point-COAwgNGw.js → file-exclamation-point-a8f3_w7a.js} +0 -0
- /package/dist/web/assets/{fish-BYrt6Xtb.js → fish-CubIIvJD.js} +0 -0
- /package/dist/web/assets/{fluent-Cr5VcAwh.js → fluent-DF55zQ09.js} +0 -0
- /package/dist/web/assets/{fortran-free-form-DQmKtrLa.js → fortran-free-form-DvOu8jAt.js} +0 -0
- /package/dist/web/assets/{gdscript-QTDKjzhY.js → gdscript-DCPxdLpq.js} +0 -0
- /package/dist/web/assets/{gdshader-Chr-5UYt.js → gdshader-CTZ381FO.js} +0 -0
- /package/dist/web/assets/{genie-kWHMWnt0.js → genie-C_QDtvgU.js} +0 -0
- /package/dist/web/assets/{gherkin-DtEBQFUo.js → gherkin-BcvK42tF.js} +0 -0
- /package/dist/web/assets/{github-dark-B5aiUta2.js → github-dark-C9afs_4w.js} +0 -0
- /package/dist/web/assets/{github-dark-default-BkxtsTqR.js → github-dark-default-CKGVrl3Z.js} +0 -0
- /package/dist/web/assets/{github-dark-dimmed-B9QjR_0K.js → github-dark-dimmed-C3dQZ_LC.js} +0 -0
- /package/dist/web/assets/{github-dark-high-contrast-D6bO-5c0.js → github-dark-high-contrast-DW78QukY.js} +0 -0
- /package/dist/web/assets/{github-light-Cg2ZW_CJ.js → github-light-BnP2dznS.js} +0 -0
- /package/dist/web/assets/{github-light-default-eD7qUVlQ.js → github-light-default-C2fPZzPG.js} +0 -0
- /package/dist/web/assets/{github-light-high-contrast-CFnlSVjd.js → github-light-high-contrast-BzIuUFR2.js} +0 -0
- /package/dist/web/assets/{gleam-BpYoOqIT.js → gleam-BpeKwEIO.js} +0 -0
- /package/dist/web/assets/{gn-BxwtZPau.js → gn-nUb_Mit0.js} +0 -0
- /package/dist/web/assets/{gnuplot-CRyiZDfl.js → gnuplot-D7I0v20p.js} +0 -0
- /package/dist/web/assets/{go-Dw1Qx94W.js → go-D7Qqska7.js} +0 -0
- /package/dist/web/assets/{groovy-zq_SbWk-.js → groovy-DS-pne5-.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-hard-DbbNy8-Q.js → gruvbox-dark-hard-C0W01fsq.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-medium-BQZ7Kj6C.js → gruvbox-dark-medium-Bqo6fQbg.js} +0 -0
- /package/dist/web/assets/{gruvbox-dark-soft-vNavErXi.js → gruvbox-dark-soft-D-XSM_fu.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-hard-AOFbCd7i.js → gruvbox-light-hard-IVAwMJRl.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-medium-DqtxND6H.js → gruvbox-light-medium-38ATiG7i.js} +0 -0
- /package/dist/web/assets/{gruvbox-light-soft-Cg0basu6.js → gruvbox-light-soft-DnZr3c_0.js} +0 -0
- /package/dist/web/assets/{haskell-CcsmcOez.js → haskell-uQcywkDH.js} +0 -0
- /package/dist/web/assets/{haxe-BimYY9X4.js → haxe-c6bI4_GT.js} +0 -0
- /package/dist/web/assets/{hcl-CTrXCK40.js → hcl-CQXTiEXq.js} +0 -0
- /package/dist/web/assets/{hjson-CmqqU6Qg.js → hjson-ChETf2bz.js} +0 -0
- /package/dist/web/assets/{hlsl-CuJtX_uf.js → hlsl-BMC4JGoE.js} +0 -0
- /package/dist/web/assets/{horizon-BGeSjDYJ.js → horizon-BusFlhLW.js} +0 -0
- /package/dist/web/assets/{horizon-bright-GYMgd3R0.js → horizon-bright-2oD0Mz7W.js} +0 -0
- /package/dist/web/assets/{houston--JD_5r2q.js → houston-DdbbjqFg.js} +0 -0
- /package/dist/web/assets/{hy-C9ILOiqk.js → hy-D_SOuqw9.js} +0 -0
- /package/dist/web/assets/{image-gallery-BWFpZAAP.js → image-gallery-CrM5fHKy.js} +0 -0
- /package/dist/web/assets/{imba-B6F3yus5.js → imba-BxLOHO5I.js} +0 -0
- /package/dist/web/assets/{ini-BfkQswMZ.js → ini-ox-CFcg0.js} +0 -0
- /package/dist/web/assets/{java-DEH5avQ1.js → java-Bzo8yIal.js} +0 -0
- /package/dist/web/assets/{javascript-Dr4yHuV2.js → javascript-DHuz9VXn.js} +0 -0
- /package/dist/web/assets/{json-BuR35ewf.js → json-0_y80FD5.js} +0 -0
- /package/dist/web/assets/{json5-Ny9ZiJAC.js → json5-CEQ50pGM.js} +0 -0
- /package/dist/web/assets/{jsonc-zYVuoZkR.js → jsonc-CfrLEAKA.js} +0 -0
- /package/dist/web/assets/{jsonl-B9xthb_H.js → jsonl-fKbrANC2.js} +0 -0
- /package/dist/web/assets/{jsonnet-CCoLJbMP.js → jsonnet-DKbkPaQ2.js} +0 -0
- /package/dist/web/assets/{jssm-W0GDlQXi.js → jssm-Dxxx1Hqu.js} +0 -0
- /package/dist/web/assets/{jsx-BrLJpWC-.js → jsx-DlJL-Oj3.js} +0 -0
- /package/dist/web/assets/{kanagawa-dragon-CuVwt_MO.js → kanagawa-dragon-CwN4SV1c.js} +0 -0
- /package/dist/web/assets/{kanagawa-lotus-B3W_617P.js → kanagawa-lotus-BHltayiE.js} +0 -0
- /package/dist/web/assets/{kanagawa-wave-CEx4MP5W.js → kanagawa-wave-D-KXPjw6.js} +0 -0
- /package/dist/web/assets/{kdl-DilRejnB.js → kdl-DvtoJdFo.js} +0 -0
- /package/dist/web/assets/{kotlin-Bq5JtKaa.js → kotlin-CTS4MLcx.js} +0 -0
- /package/dist/web/assets/{kusto-Bhp2KYog.js → kusto-HtSE_sKm.js} +0 -0
- /package/dist/web/assets/{laserwave-WA_7c5hu.js → laserwave-DaXZG9ge.js} +0 -0
- /package/dist/web/assets/{lean-AEGcXwS-.js → lean-BYtGUfWx.js} +0 -0
- /package/dist/web/assets/{less-NPqH-26u.js → less-BoxmU_Mk.js} +0 -0
- /package/dist/web/assets/{lib-BFetOULu.js → lib-BzxL1ARZ.js} +0 -0
- /package/dist/web/assets/{libesm-tR4XlhMj.js → libesm-ah-mr-sh.js} +0 -0
- /package/dist/web/assets/{light-plus-B4xW7cgY.js → light-plus-Bt5CyUuo.js} +0 -0
- /package/dist/web/assets/{llvm-CPyWNcDE.js → llvm-CSdk9q9V.js} +0 -0
- /package/dist/web/assets/{log-CeQmiQSz.js → log-B-kp_3CL.js} +0 -0
- /package/dist/web/assets/{logo-BO0VrjBm.js → logo-DdIsXFDO.js} +0 -0
- /package/dist/web/assets/{luau-B6GT9kA1.js → luau-DUBpLlzQ.js} +0 -0
- /package/dist/web/assets/{make-CwfmN7e_.js → make-B3JVjoW_.js} +0 -0
- /package/dist/web/assets/{markdown-Bbe4FlLp.js → markdown-Seon0vHs.js} +0 -0
- /package/dist/web/assets/{material-theme-iD9XbfsB.js → material-theme-Nq-ChX1P.js} +0 -0
- /package/dist/web/assets/{material-theme-darker-u7M3XGe7.js → material-theme-darker-CPV4ayXJ.js} +0 -0
- /package/dist/web/assets/{material-theme-lighter-BGdI48zn.js → material-theme-lighter-0kyjYqEd.js} +0 -0
- /package/dist/web/assets/{material-theme-ocean-_mk62F5q.js → material-theme-ocean-DGpAp5co.js} +0 -0
- /package/dist/web/assets/{material-theme-palenight-CxFbDyAu.js → material-theme-palenight-B0AUn0NG.js} +0 -0
- /package/dist/web/assets/{matlab-Z0RUE3V_.js → matlab-C2waytxs.js} +0 -0
- /package/dist/web/assets/{mdx-BZ5wPYzV.js → mdx-DAis6R6I.js} +0 -0
- /package/dist/web/assets/{mermaid-CY1_aP-h.js → mermaid-_rnT2YGD.js} +0 -0
- /package/dist/web/assets/{min-dark-DKWtxOre.js → min-dark-BSSjchUB.js} +0 -0
- /package/dist/web/assets/{min-light-e1iwHDfP.js → min-light-DomAFQsa.js} +0 -0
- /package/dist/web/assets/{mipsasm-BiDfQ4ir.js → mipsasm-bBgtvPyG.js} +0 -0
- /package/dist/web/assets/{mojo-DteFEHsj.js → mojo-Cp9BayW0.js} +0 -0
- /package/dist/web/assets/{monokai-BgPbE9xZ.js → monokai-CL_9gbmn.js} +0 -0
- /package/dist/web/assets/{moonbit-mEbRp-mA.js → moonbit-BS_fcr-P.js} +0 -0
- /package/dist/web/assets/{move-CogqZw7A.js → move-C79AH45U.js} +0 -0
- /package/dist/web/assets/{narrat-BD2qjmy1.js → narrat-CJ5s3BDf.js} +0 -0
- /package/dist/web/assets/{nextflow-groovy-BNX8GOD_.js → nextflow-groovy-ZHa4JrPJ.js} +0 -0
- /package/dist/web/assets/{night-owl-ed3GbziD.js → night-owl-Jkf9zFQK.js} +0 -0
- /package/dist/web/assets/{night-owl-light-B0j7O0Wn.js → night-owl-light-DRPeaG5J.js} +0 -0
- /package/dist/web/assets/{nix-DFDbcQQm.js → nix-D2Jet9DN.js} +0 -0
- /package/dist/web/assets/{nord-D93TxEQi.js → nord-CREVs22j.js} +0 -0
- /package/dist/web/assets/{nushell-3_H_XKja.js → nushell-CAiDi_mR.js} +0 -0
- /package/dist/web/assets/{objective-c-BM3u6nlm.js → objective-c-CVkmJkC1.js} +0 -0
- /package/dist/web/assets/{objective-cpp-DZ73h42g.js → objective-cpp-DIKDO1Ly.js} +0 -0
- /package/dist/web/assets/{ocaml-B1PU5gmh.js → ocaml-R9qtGQhs.js} +0 -0
- /package/dist/web/assets/{odin-D14L6aRL.js → odin-DTYNQqVr.js} +0 -0
- /package/dist/web/assets/{one-dark-pro-Bj-94Z8v.js → one-dark-pro-BSLWUJn5.js} +0 -0
- /package/dist/web/assets/{one-light-DTdGWyOZ.js → one-light-DbkRXG3t.js} +0 -0
- /package/dist/web/assets/{openscad-Ciq554Sj.js → openscad-BlPcSnmT.js} +0 -0
- /package/dist/web/assets/{pascal-Bfd4qdGi.js → pascal-B1Y46ZZB.js} +0 -0
- /package/dist/web/assets/{pkl-CTurDUfj.js → pkl-BhJSJbFv.js} +0 -0
- /package/dist/web/assets/{plastic-BLQfZXQp.js → plastic-BzxsCTpi.js} +0 -0
- /package/dist/web/assets/{plsql-DyYfP9kQ.js → plsql-DBba7dyP.js} +0 -0
- /package/dist/web/assets/{po-CgXdT8vQ.js → po-BvgLlnNo.js} +0 -0
- /package/dist/web/assets/{poimandres-DOg5fK6s.js → poimandres-DUaI_1JP.js} +0 -0
- /package/dist/web/assets/{polar-Dv4aoL6X.js → polar-C7uILwJn.js} +0 -0
- /package/dist/web/assets/{postcss-DxugHnJD.js → postcss-BgJlpFdw.js} +0 -0
- /package/dist/web/assets/{powerquery-CR3kzRyX.js → powerquery-Dn1MIpZu.js} +0 -0
- /package/dist/web/assets/{powershell-BE91xJQB.js → powershell-Bo-DYzOS.js} +0 -0
- /package/dist/web/assets/{prisma-B_3yUlRQ.js → prisma-CTLs5IdT.js} +0 -0
- /package/dist/web/assets/{prolog-DcT781Ht.js → prolog-QRGun3eG.js} +0 -0
- /package/dist/web/assets/{proto-CSf-w5k2.js → proto-B7PI7apn.js} +0 -0
- /package/dist/web/assets/{puppet-CLGO6QWl.js → puppet-CBq0tLrP.js} +0 -0
- /package/dist/web/assets/{purescript-Bah2McKd.js → purescript-DFewOQb2.js} +0 -0
- /package/dist/web/assets/{python-DUH8qy7N.js → python-iWuxKXvT.js} +0 -0
- /package/dist/web/assets/{qmldir-YIsHnjKq.js → qmldir-cXiW3AsF.js} +0 -0
- /package/dist/web/assets/{qss-CLmoVNA4.js → qss-DASwAbBL.js} +0 -0
- /package/dist/web/assets/{r-Pon-b-vz.js → r-TU2eaIPw.js} +0 -0
- /package/dist/web/assets/{racket-BA09P5Kg.js → racket-SkRKqrLY.js} +0 -0
- /package/dist/web/assets/{raku-DlpzU7Fe.js → raku-D31lthkZ.js} +0 -0
- /package/dist/web/assets/{react-w6wINGnd.js → react-I_EwPIZi.js} +0 -0
- /package/dist/web/assets/{red-l7VuCR6d.js → red-CTcQZevb.js} +0 -0
- /package/dist/web/assets/{reg-g_k7gQEr.js → reg-D8YAsTGf.js} +0 -0
- /package/dist/web/assets/{regexp-CT17B7I7.js → regexp-CD2uvAWx.js} +0 -0
- /package/dist/web/assets/{rel-DkPCS7Bp.js → rel-CAaI-1fg.js} +0 -0
- /package/dist/web/assets/{resolve-theme-DdB7Yo5Z.js → resolve-theme-CJvkL8hU.js} +0 -0
- /package/dist/web/assets/{riscv-CYuFTw30.js → riscv-DR-hJVIj.js} +0 -0
- /package/dist/web/assets/{ron-cY7u_yEu.js → ron-CuUCJ0n4.js} +0 -0
- /package/dist/web/assets/{rose-pine-Cr_h_Ubo.js → rose-pine-aFHxW48i.js} +0 -0
- /package/dist/web/assets/{rose-pine-dawn-rRewaSZg.js → rose-pine-dawn-Bqz2sBRY.js} +0 -0
- /package/dist/web/assets/{rose-pine-moon-3VX5Xwca.js → rose-pine-moon-wqyh0an4.js} +0 -0
- /package/dist/web/assets/{rosmsg-Du4ie5PU.js → rosmsg-CI1ywimO.js} +0 -0
- /package/dist/web/assets/{rust-B7xWL-Eh.js → rust-p7Imk4G_.js} +0 -0
- /package/dist/web/assets/{sass-oXbOG8de.js → sass-C8JfaqCV.js} +0 -0
- /package/dist/web/assets/{save-C4z1p51e.js → save-DRPrDfTt.js} +0 -0
- /package/dist/web/assets/{scala-nlZJhKHf.js → scala-DNxCW1By.js} +0 -0
- /package/dist/web/assets/{scheme-FZ9_Zy8D.js → scheme-BYuUPJoL.js} +0 -0
- /package/dist/web/assets/{sdbl-D_lyvXiu.js → sdbl-DbPeJPUr.js} +0 -0
- /package/dist/web/assets/{shellscript-DRWRboAd.js → shellscript-DMiHlR3q.js} +0 -0
- /package/dist/web/assets/{shield-check-C5laObJv.js → shield-check-8GObuUAn.js} +0 -0
- /package/dist/web/assets/{shield-off-DULFtyLS.js → shield-off-egKAZEwR.js} +0 -0
- /package/dist/web/assets/{slack-dark-BGk2uVHu.js → slack-dark-C4xGcQXI.js} +0 -0
- /package/dist/web/assets/{slack-ochin-PAoSX041.js → slack-ochin-BzgRt_wm.js} +0 -0
- /package/dist/web/assets/{smalltalk-qP0zvICo.js → smalltalk-LDeL9PYH.js} +0 -0
- /package/dist/web/assets/{snazzy-light-B7uccpH6.js → snazzy-light-CXE7a5j_.js} +0 -0
- /package/dist/web/assets/{solarized-dark-CbICLZs3.js → solarized-dark-DgUhY74x.js} +0 -0
- /package/dist/web/assets/{solarized-light-CprWhyjj.js → solarized-light-DD9C-H-9.js} +0 -0
- /package/dist/web/assets/{solidity-B7pcm2K6.js → solidity-CAbDB4D4.js} +0 -0
- /package/dist/web/assets/{splunk-Co2LL4J_.js → splunk-kQREPfIc.js} +0 -0
- /package/dist/web/assets/{sql-cETIdGnT.js → sql-CBzeNAD8.js} +0 -0
- /package/dist/web/assets/{ssh-config-DETG6JCD.js → ssh-config-L1euxrsM.js} +0 -0
- /package/dist/web/assets/{stylus-tmTre2-O.js → stylus-DvBnYniP.js} +0 -0
- /package/dist/web/assets/{swift-BeKQbULV.js → swift-DF5NDQp6.js} +0 -0
- /package/dist/web/assets/{synthwave-84-eCL4-Xdn.js → synthwave-84-DbBhB_Hq.js} +0 -0
- /package/dist/web/assets/{system-verilog-CviwWvEx.js → system-verilog-D8s2ZW_k.js} +0 -0
- /package/dist/web/assets/{systemd-WuoFIbBS.js → systemd-a5-XZJsG.js} +0 -0
- /package/dist/web/assets/{table-CHmxuh6M.js → table-BX2ZrJfM.js} +0 -0
- /package/dist/web/assets/{talonscript-B9VBu7VI.js → talonscript-BLa0URTW.js} +0 -0
- /package/dist/web/assets/{tasl-CN7r11ky.js → tasl-DIoV4XcC.js} +0 -0
- /package/dist/web/assets/{tcl-e1cdhe3h.js → tcl-ztYAdXfQ.js} +0 -0
- /package/dist/web/assets/{terraform-BEP0mfa_.js → terraform-DvpCCiOZ.js} +0 -0
- /package/dist/web/assets/{tokyo-night-BfA_dA37.js → tokyo-night-BkHlYUv0.js} +0 -0
- /package/dist/web/assets/{toml-BrDIfNnt.js → toml-acIrxos6.js} +0 -0
- /package/dist/web/assets/{tsv-CyfGyE4i.js → tsv-BJB5EVCT.js} +0 -0
- /package/dist/web/assets/{tsx-BrFA8Iwf.js → tsx-B1MmXGT3.js} +0 -0
- /package/dist/web/assets/{turtle-9GBtR0mP.js → turtle-DFceTiqd.js} +0 -0
- /package/dist/web/assets/{typescript-BuTRZ65V.js → typescript-Dz9wU5Y1.js} +0 -0
- /package/dist/web/assets/{typespec-CRiNNRXF.js → typespec-CPuYZijj.js} +0 -0
- /package/dist/web/assets/{typst-CmpTnnkl.js → typst-CfsCIYv8.js} +0 -0
- /package/dist/web/assets/{utils-B9H0d3Fh.js → utils-DoY4yTP-.js} +0 -0
- /package/dist/web/assets/{v-DnIi2aab.js → v-CGo0jcXA.js} +0 -0
- /package/dist/web/assets/{vala-B7VxCNZP.js → vala-AsSR0hrb.js} +0 -0
- /package/dist/web/assets/{vb-DGoQ3pBT.js → vb-C7tvbXv1.js} +0 -0
- /package/dist/web/assets/{vendor-xterm-ClRVIJ-k.js → vendor-xterm-BeaIjRrv.js} +0 -0
- /package/dist/web/assets/{verilog-zGOC4qwV.js → verilog--a33ND-p.js} +0 -0
- /package/dist/web/assets/{vesper-DXR9-06q.js → vesper-B_Z0h4pn.js} +0 -0
- /package/dist/web/assets/{vhdl-CCO0oz0l.js → vhdl-CWL5dZPN.js} +0 -0
- /package/dist/web/assets/{viml-Dxwr-QNN.js → viml-BglmT14Y.js} +0 -0
- /package/dist/web/assets/{vitesse-black-Bhah7RxE.js → vitesse-black-RKFsSqQI.js} +0 -0
- /package/dist/web/assets/{vitesse-dark-CGK6XVwc.js → vitesse-dark-D7zDWNmc.js} +0 -0
- /package/dist/web/assets/{vitesse-light-ne5kfw8h.js → vitesse-light-MAp87W5b.js} +0 -0
- /package/dist/web/assets/{vyper-ErDFqVoP.js → vyper-CgCBYg1G.js} +0 -0
- /package/dist/web/assets/{wasm-DtWvUWGr.js → wasm-BOG7ZeTT.js} +0 -0
- /package/dist/web/assets/{wasm-Bd258nS1.js → wasm-Bwl4txwp.js} +0 -0
- /package/dist/web/assets/{wenyan-EZe1IEYT.js → wenyan-DpShPKXZ.js} +0 -0
- /package/dist/web/assets/{wgsl-B1o5_I_9.js → wgsl-BakcCblK.js} +0 -0
- /package/dist/web/assets/{wikitext-C_rwp8An.js → wikitext-NroRWVAO.js} +0 -0
- /package/dist/web/assets/{wit-gbYtF3rY.js → wit-B5CM2pg6.js} +0 -0
- /package/dist/web/assets/{wolfram-DrcQFe2n.js → wolfram-7MTFBJpR.js} +0 -0
- /package/dist/web/assets/{x-B1qHmLdR.js → x-BcqfTY_7.js} +0 -0
- /package/dist/web/assets/{yaml-CzDAObLX.js → yaml-Bwr01G2K.js} +0 -0
- /package/dist/web/assets/{zenscript-B-J1phtg.js → zenscript-Dj8GATvi.js} +0 -0
- /package/dist/web/assets/{zig-BlzNia0O.js → zig-LAE-fbS-.js} +0 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { useEffect, useRef, useState, useCallback } from "react";
|
|
2
|
+
import { usePanelStore } from "@/stores/panel-store";
|
|
3
|
+
import {
|
|
4
|
+
RUN_IN_TERMINAL_EVENT,
|
|
5
|
+
RUN_IN_TERMINAL_ACK_EVENT,
|
|
6
|
+
type RunInTerminalDetail,
|
|
7
|
+
} from "@/lib/run-in-terminal";
|
|
8
|
+
|
|
9
|
+
interface UseTerminalCommandQueueOptions {
|
|
10
|
+
/** Tab ID — used to drop `pendingCommand` from persisted metadata once consumed. */
|
|
11
|
+
tabId?: string;
|
|
12
|
+
metadata?: Record<string, unknown>;
|
|
13
|
+
/** From `useTerminal` — the shell has printed its prompt and can take input. */
|
|
14
|
+
shellReady: boolean;
|
|
15
|
+
sendData: (data: string) => void;
|
|
16
|
+
focusTerminal: () => void;
|
|
17
|
+
containerRef: React.RefObject<HTMLDivElement | null>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Smallest box treated as a real terminal. Not `> 0`: a terminal parked in the
|
|
22
|
+
* zero-sized tab pool (dock closed, tab inactive) still measures 8×8 from its own
|
|
23
|
+
* padding, and a hidden terminal claiming the command would swallow it silently.
|
|
24
|
+
*/
|
|
25
|
+
const MIN_VISIBLE_PX = 40;
|
|
26
|
+
|
|
27
|
+
/** On screen? Must be laid out at a usable size and inside the viewport. */
|
|
28
|
+
function isOnScreen(el: HTMLElement | null): boolean {
|
|
29
|
+
if (!el) return false;
|
|
30
|
+
// `visibility: hidden` on the tab pool is the crisp signal where supported.
|
|
31
|
+
if (typeof el.checkVisibility === "function" && !el.checkVisibility({ visibilityProperty: true })) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const r = el.getBoundingClientRect();
|
|
35
|
+
if (r.width < MIN_VISIBLE_PX || r.height < MIN_VISIBLE_PX) return false;
|
|
36
|
+
return r.bottom > 0 && r.right > 0 && r.top < window.innerHeight && r.left < window.innerWidth;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Type a command into this terminal, deferred until the shell can accept it.
|
|
41
|
+
*
|
|
42
|
+
* Fed from two directions: `metadata.pendingCommand` on a terminal that was just
|
|
43
|
+
* opened to carry the command, and the `ppm:run-in-terminal` event for a terminal
|
|
44
|
+
* already on screen. Both land in the same one-slot queue, which flushes once
|
|
45
|
+
* `shellReady` turns true — a shell still running its startup files drops
|
|
46
|
+
* anything typed at it, which is why the command cannot be sent on connect.
|
|
47
|
+
*
|
|
48
|
+
* The command is typed WITHOUT a trailing newline: the user reads it and presses
|
|
49
|
+
* Enter. Same semantics as a paste, so a multi-line block behaves like pasting one.
|
|
50
|
+
*/
|
|
51
|
+
export function useTerminalCommandQueue({
|
|
52
|
+
tabId,
|
|
53
|
+
metadata,
|
|
54
|
+
shellReady,
|
|
55
|
+
sendData,
|
|
56
|
+
focusTerminal,
|
|
57
|
+
containerRef,
|
|
58
|
+
}: UseTerminalCommandQueueOptions): void {
|
|
59
|
+
/** Latest command awaiting a ready shell — latest wins, since queued commands
|
|
60
|
+
* typed back-to-back without a newline would merge into one garbled line. */
|
|
61
|
+
const pendingRef = useRef<string | null>(null);
|
|
62
|
+
/** Incremented per queued command so the flush effect re-runs on repeat clicks. */
|
|
63
|
+
const [revision, setRevision] = useState(0);
|
|
64
|
+
|
|
65
|
+
const queue = useCallback((command: string) => {
|
|
66
|
+
pendingRef.current = command;
|
|
67
|
+
setRevision((n) => n + 1);
|
|
68
|
+
}, []);
|
|
69
|
+
|
|
70
|
+
// Command handed over by whoever opened this tab. Stripped from the tab's
|
|
71
|
+
// metadata immediately: metadata is persisted to localStorage, and a page
|
|
72
|
+
// reload must not re-inject a command the user already dealt with.
|
|
73
|
+
const pendingCommand = metadata?.pendingCommand;
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (typeof pendingCommand !== "string" || !pendingCommand) return;
|
|
76
|
+
queue(pendingCommand);
|
|
77
|
+
if (tabId) {
|
|
78
|
+
const rest = { ...(metadata ?? {}) };
|
|
79
|
+
delete rest.pendingCommand;
|
|
80
|
+
usePanelStore.getState().updateTab(tabId, { metadata: rest });
|
|
81
|
+
}
|
|
82
|
+
// `metadata` is intentionally not a dependency — the strip above already
|
|
83
|
+
// makes this fire exactly once per handed-over command.
|
|
84
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
85
|
+
}, [pendingCommand, tabId, queue]);
|
|
86
|
+
|
|
87
|
+
// Claim commands aimed at "whatever terminal the user is looking at".
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const onRun = (event: Event) => {
|
|
90
|
+
const detail = (event as CustomEvent<RunInTerminalDetail>).detail;
|
|
91
|
+
if (!detail?.command) return;
|
|
92
|
+
if (!isOnScreen(containerRef.current)) return;
|
|
93
|
+
queue(detail.command);
|
|
94
|
+
window.dispatchEvent(new Event(RUN_IN_TERMINAL_ACK_EVENT));
|
|
95
|
+
};
|
|
96
|
+
window.addEventListener(RUN_IN_TERMINAL_EVENT, onRun);
|
|
97
|
+
return () => window.removeEventListener(RUN_IN_TERMINAL_EVENT, onRun);
|
|
98
|
+
}, [containerRef, queue]);
|
|
99
|
+
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
if (!shellReady) return;
|
|
102
|
+
const command = pendingRef.current;
|
|
103
|
+
if (!command) return;
|
|
104
|
+
pendingRef.current = null;
|
|
105
|
+
sendData(command);
|
|
106
|
+
focusTerminal();
|
|
107
|
+
}, [shellReady, revision, sendData, focusTerminal]);
|
|
108
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useRef, useCallback, useState } from "react";
|
|
2
2
|
import { Terminal, type ITheme } from "@xterm/xterm";
|
|
3
|
+
import { withWsAuth } from "@/lib/ws-auth";
|
|
3
4
|
import { FitAddon } from "@xterm/addon-fit";
|
|
4
5
|
import { WebLinksAddon } from "@xterm/addon-web-links";
|
|
5
6
|
import { WebglAddon } from "@xterm/addon-webgl";
|
|
@@ -21,6 +22,8 @@ function currentXtermTheme(): ITheme {
|
|
|
21
22
|
interface UseTerminalOptions {
|
|
22
23
|
sessionId: string;
|
|
23
24
|
projectName?: string;
|
|
25
|
+
/** Absolute start directory for a brand-new shell; ignored when re-attaching to a session. */
|
|
26
|
+
cwd?: string;
|
|
24
27
|
containerRef: React.RefObject<HTMLDivElement | null>;
|
|
25
28
|
/** Stable tab ID for persisting session across reload */
|
|
26
29
|
tabId?: string;
|
|
@@ -30,10 +33,14 @@ interface UseTerminalReturn {
|
|
|
30
33
|
connected: boolean;
|
|
31
34
|
reconnecting: boolean;
|
|
32
35
|
exited: boolean;
|
|
36
|
+
/** True once the shell has printed its prompt and stopped writing — safe to inject input. */
|
|
37
|
+
shellReady: boolean;
|
|
33
38
|
sendData: (data: string) => void;
|
|
34
39
|
getSelection: () => string;
|
|
35
40
|
/** Read buffer from last command start to current cursor (for "Send to Chat"). */
|
|
36
41
|
getLastCommandOutput: () => string;
|
|
42
|
+
/** The prompt line the last command was typed on, empty until one is entered. */
|
|
43
|
+
getLastCommand: () => string;
|
|
37
44
|
/** URLs present in the scrollback, most recent first. */
|
|
38
45
|
getBufferUrls: () => string[];
|
|
39
46
|
restart: () => void;
|
|
@@ -47,6 +54,11 @@ const URL_SCAN_MAX_ROWS = 5000;
|
|
|
47
54
|
/** Quiet period before refitting to a new container size, in ms. */
|
|
48
55
|
const RESIZE_SETTLE_MS = 100;
|
|
49
56
|
|
|
57
|
+
/** Quiet period after the last PTY output that means the prompt is printed and the shell is idle. */
|
|
58
|
+
const SHELL_READY_QUIET_MS = 250;
|
|
59
|
+
/** Upper bound on waiting for that prompt — a shell that prints nothing must not block input forever. */
|
|
60
|
+
const SHELL_READY_MAX_MS = 3000;
|
|
61
|
+
|
|
50
62
|
const RESIZE_PREFIX = "\x01RESIZE:";
|
|
51
63
|
const PING_MSG = "\x01PING";
|
|
52
64
|
const PONG_MSG = "\x01PONG";
|
|
@@ -72,6 +84,10 @@ export function useTerminal(
|
|
|
72
84
|
const [connected, setConnected] = useState(false);
|
|
73
85
|
const [reconnecting, setReconnecting] = useState(false);
|
|
74
86
|
const [exited, setExited] = useState(false);
|
|
87
|
+
/** A brand-new shell needs to finish booting before it accepts injected input. */
|
|
88
|
+
const [shellReady, setShellReady] = useState(false);
|
|
89
|
+
const readyQuietTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
90
|
+
const readyMaxTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
75
91
|
// Restore persisted session ID from localStorage (survives page reload)
|
|
76
92
|
const storageKey = options.tabId ? `ppm:terminal-session:${options.tabId}` : null;
|
|
77
93
|
const initialSessionId = (() => {
|
|
@@ -83,6 +99,9 @@ export function useTerminal(
|
|
|
83
99
|
const actualSessionId = useRef(initialSessionId);
|
|
84
100
|
/** Absolute row where last command output starts (set when user presses Enter) */
|
|
85
101
|
const commandStartRow = useRef(0);
|
|
102
|
+
/** Absolute row of the prompt line that command was typed on, -1 before any Enter.
|
|
103
|
+
* Output alone reads as an orphan in chat — the command is what gives it meaning. */
|
|
104
|
+
const commandRow = useRef(-1);
|
|
86
105
|
|
|
87
106
|
const sendData = useCallback((data: string) => {
|
|
88
107
|
const ws = wsRef.current;
|
|
@@ -91,6 +110,12 @@ export function useTerminal(
|
|
|
91
110
|
}
|
|
92
111
|
}, []);
|
|
93
112
|
|
|
113
|
+
/** Debounce PTY output: the shell counts as booted once it goes quiet. */
|
|
114
|
+
const noteOutput = useCallback(() => {
|
|
115
|
+
if (readyQuietTimer.current) clearTimeout(readyQuietTimer.current);
|
|
116
|
+
readyQuietTimer.current = setTimeout(() => setShellReady(true), SHELL_READY_QUIET_MS);
|
|
117
|
+
}, []);
|
|
118
|
+
|
|
94
119
|
const getSelection = useCallback(() => {
|
|
95
120
|
return termRef.current?.getSelection() ?? "";
|
|
96
121
|
}, []);
|
|
@@ -111,6 +136,12 @@ export function useTerminal(
|
|
|
111
136
|
return lines.join("\n");
|
|
112
137
|
}, []);
|
|
113
138
|
|
|
139
|
+
const getLastCommand = useCallback(() => {
|
|
140
|
+
const term = termRef.current;
|
|
141
|
+
if (!term || commandRow.current < 0) return "";
|
|
142
|
+
return term.buffer.active.getLine(commandRow.current)?.translateToString(true).trimEnd() ?? "";
|
|
143
|
+
}, []);
|
|
144
|
+
|
|
114
145
|
/**
|
|
115
146
|
* Collect URLs from the scrollback.
|
|
116
147
|
*
|
|
@@ -172,6 +203,7 @@ export function useTerminal(
|
|
|
172
203
|
if (storageKey) { try { localStorage.removeItem(storageKey); } catch { /* */ } }
|
|
173
204
|
reconnectAttempts.current = 0;
|
|
174
205
|
setExited(false);
|
|
206
|
+
setShellReady(false);
|
|
175
207
|
setConnected(false);
|
|
176
208
|
setReconnecting(false);
|
|
177
209
|
// connectWs will be called after this via setTimeout to allow state to settle
|
|
@@ -198,7 +230,8 @@ export function useTerminal(
|
|
|
198
230
|
const projectName = options.projectName ?? "";
|
|
199
231
|
// Use actual session ID from server on reconnect (not "new")
|
|
200
232
|
const sid = actualSessionId.current;
|
|
201
|
-
const
|
|
233
|
+
const cwdQuery = sid === "new" && options.cwd ? `?cwd=${encodeURIComponent(options.cwd)}` : "";
|
|
234
|
+
const path = withWsAuth(`/ws/project/${encodeURIComponent(projectName)}/terminal/${sid}${cwdQuery}`);
|
|
202
235
|
// Local dev over http: connect directly to backend (port 8081) to bypass
|
|
203
236
|
// Vite's dev proxy which has unreliable WebSocket upgrade handling. Over https
|
|
204
237
|
// (e.g. a Cloudflare tunnel) port 8081 isn't reachable and ws:// is blocked as
|
|
@@ -220,6 +253,11 @@ export function useTerminal(
|
|
|
220
253
|
setConnected(true);
|
|
221
254
|
setReconnecting(false);
|
|
222
255
|
reconnectAttempts.current = 0;
|
|
256
|
+
// A re-attach lands on a shell that booted long ago; only a fresh session
|
|
257
|
+
// has to wait for a prompt.
|
|
258
|
+
if (actualSessionId.current === "new") setShellReady(false);
|
|
259
|
+
if (readyMaxTimer.current) clearTimeout(readyMaxTimer.current);
|
|
260
|
+
readyMaxTimer.current = setTimeout(() => setShellReady(true), SHELL_READY_MAX_MS);
|
|
223
261
|
sendResize();
|
|
224
262
|
};
|
|
225
263
|
|
|
@@ -255,6 +293,7 @@ export function useTerminal(
|
|
|
255
293
|
// Not JSON, write as terminal output
|
|
256
294
|
}
|
|
257
295
|
}
|
|
296
|
+
noteOutput();
|
|
258
297
|
term.write(event.data);
|
|
259
298
|
}
|
|
260
299
|
};
|
|
@@ -347,7 +386,8 @@ export function useTerminal(
|
|
|
347
386
|
// When user presses Enter, mark next row as command output start
|
|
348
387
|
if (data.includes("\r") || data.includes("\n")) {
|
|
349
388
|
const buf = term.buffer.active;
|
|
350
|
-
|
|
389
|
+
commandRow.current = buf.baseY + buf.cursorY;
|
|
390
|
+
commandStartRow.current = commandRow.current + 1;
|
|
351
391
|
}
|
|
352
392
|
const ws = wsRef.current;
|
|
353
393
|
if (ws?.readyState === WebSocket.OPEN) {
|
|
@@ -399,6 +439,8 @@ export function useTerminal(
|
|
|
399
439
|
clearInterval(heartbeatInterval);
|
|
400
440
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
401
441
|
if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
|
|
442
|
+
if (readyQuietTimer.current) clearTimeout(readyQuietTimer.current);
|
|
443
|
+
if (readyMaxTimer.current) clearTimeout(readyMaxTimer.current);
|
|
402
444
|
wsRef.current?.close();
|
|
403
445
|
wsRef.current = null;
|
|
404
446
|
term.dispose();
|
|
@@ -407,5 +449,5 @@ export function useTerminal(
|
|
|
407
449
|
};
|
|
408
450
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
409
451
|
|
|
410
|
-
return { connected, reconnecting, exited, sendData, getSelection, getLastCommandOutput, getBufferUrls, restart };
|
|
452
|
+
return { connected, reconnecting, exited, shellReady, sendData, getSelection, getLastCommandOutput, getLastCommand, getBufferUrls, restart };
|
|
411
453
|
}
|
package/src/web/lib/fs-api.ts
CHANGED
|
@@ -182,4 +182,13 @@ export const fsApi = {
|
|
|
182
182
|
});
|
|
183
183
|
return `/api/fs/raw?path=${q(path)}&download=true&dl_token=${q(token)}`;
|
|
184
184
|
},
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Target for a streamed `PUT` upload — `path` is the full destination file path. Not used
|
|
188
|
+
* through `request()`: the upload helper needs `XMLHttpRequest` for progress events, which
|
|
189
|
+
* `fetch` cannot give.
|
|
190
|
+
*/
|
|
191
|
+
uploadUrl(path: string, overwrite: boolean): string {
|
|
192
|
+
return `/api/fs/upload?path=${q(path)}&overwrite=${overwrite ? "1" : "0"}`;
|
|
193
|
+
},
|
|
185
194
|
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-file upload over `XMLHttpRequest`. `fetch` gives no upload progress events, so the
|
|
3
|
+
* queue needs XHR for the per-file percentage the toast shows; everything else about the
|
|
4
|
+
* request (auth header, error shape) mirrors `fs-api.ts`'s `request()`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getAuthToken } from "./api-client";
|
|
8
|
+
import { fsApi, FsError } from "./fs-api";
|
|
9
|
+
|
|
10
|
+
export interface UploadProgress {
|
|
11
|
+
loaded: number;
|
|
12
|
+
total: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface UploadResponseBody {
|
|
16
|
+
ok?: boolean;
|
|
17
|
+
data?: { path: string; size: number };
|
|
18
|
+
error?: string;
|
|
19
|
+
code?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resolves with the uploaded `{ path, size }`. Rejects with `FsError` for a server refusal
|
|
24
|
+
* (409 `EEXIST`, 403 `EDENIED`, …) and a plain `Error` for a network failure — the caller
|
|
25
|
+
* tells them apart with `instanceof FsError`, same as every other `fs-api` call.
|
|
26
|
+
*/
|
|
27
|
+
export function uploadFileXhr(
|
|
28
|
+
path: string,
|
|
29
|
+
file: File,
|
|
30
|
+
overwrite: boolean,
|
|
31
|
+
onProgress: (progress: UploadProgress) => void,
|
|
32
|
+
): Promise<{ path: string; size: number }> {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const xhr = new XMLHttpRequest();
|
|
35
|
+
xhr.open("PUT", fsApi.uploadUrl(path, overwrite));
|
|
36
|
+
const token = getAuthToken();
|
|
37
|
+
if (token) xhr.setRequestHeader("Authorization", `Bearer ${token}`);
|
|
38
|
+
xhr.setRequestHeader("x-ppm-client", "web");
|
|
39
|
+
|
|
40
|
+
xhr.upload.onprogress = (e) => {
|
|
41
|
+
if (e.lengthComputable) onProgress({ loaded: e.loaded, total: e.total });
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
xhr.onload = () => {
|
|
45
|
+
let json: UploadResponseBody = {};
|
|
46
|
+
try {
|
|
47
|
+
json = JSON.parse(xhr.responseText);
|
|
48
|
+
} catch {
|
|
49
|
+
// Falls through to the status-based rejection below.
|
|
50
|
+
}
|
|
51
|
+
if (xhr.status >= 200 && xhr.status < 300 && json.ok !== false && json.data) {
|
|
52
|
+
resolve(json.data);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
reject(new FsError(json.error ?? `HTTP ${xhr.status}`, json.code ?? "EUNKNOWN", xhr.status));
|
|
56
|
+
};
|
|
57
|
+
xhr.onerror = () => reject(new Error("Network error during upload"));
|
|
58
|
+
xhr.onabort = () => reject(new DOMException("Upload aborted", "AbortError"));
|
|
59
|
+
|
|
60
|
+
xhr.send(file);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL builders for media the browser fetches on its own (`<video src>`, `<audio src>`).
|
|
3
|
+
*
|
|
4
|
+
* Those elements cannot send an Authorization header, so the session token rides in
|
|
5
|
+
* the query string — the server only accepts `?token=` on these exact routes.
|
|
6
|
+
* Streaming straight from the URL (instead of fetching a blob first) lets the browser
|
|
7
|
+
* issue Range requests, so a multi-GB file starts playing immediately and seeking
|
|
8
|
+
* costs one small request.
|
|
9
|
+
*/
|
|
10
|
+
import { api, getAuthToken, projectUrl } from "@/lib/api-client";
|
|
11
|
+
|
|
12
|
+
/** Absolute (`/x`, `C:\x`) paths belong to the whole-disk `/api/fs` routes, relative ones to the project. */
|
|
13
|
+
export function isExternalPath(filePath: string): boolean {
|
|
14
|
+
return /^(\/|[A-Za-z]:[/\\])/.test(filePath);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** `/api/fs/<route>` or `/api/project/<name>/files/<route>` depending on the path kind. */
|
|
18
|
+
function mediaBase(route: "raw" | "transcode" | "probe", filePath: string, projectName: string): string {
|
|
19
|
+
const prefix = isExternalPath(filePath) ? "/api/fs" : `${projectUrl(projectName)}/files`;
|
|
20
|
+
return `${prefix}/${route}?path=${encodeURIComponent(filePath)}`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function withToken(url: string): string {
|
|
24
|
+
const token = getAuthToken();
|
|
25
|
+
return token ? `${url}&token=${encodeURIComponent(token)}` : url;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Direct, Range-capable URL of the file as stored on disk. */
|
|
29
|
+
export function rawMediaUrl(filePath: string, projectName: string): string {
|
|
30
|
+
return withToken(mediaBase("raw", filePath, projectName));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Live ffmpeg → fragmented-MP4 stream starting at `start` seconds. */
|
|
34
|
+
export function transcodeMediaUrl(filePath: string, projectName: string, start = 0): string {
|
|
35
|
+
const base = mediaBase("transcode", filePath, projectName);
|
|
36
|
+
return withToken(start > 0 ? `${base}&start=${start.toFixed(3)}` : base);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface MediaProbeInfo {
|
|
40
|
+
transcodable: boolean;
|
|
41
|
+
encoder: string | null;
|
|
42
|
+
duration: number | null;
|
|
43
|
+
container: string | null;
|
|
44
|
+
video: { codec: string; width?: number; height?: number } | null;
|
|
45
|
+
audio: { codec: string } | null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Ask the server what the file contains and whether it can transcode it. */
|
|
49
|
+
export function probeMedia(filePath: string, projectName: string): Promise<MediaProbeInfo> {
|
|
50
|
+
return api.get<MediaProbeInfo>(mediaBase("probe", filePath, projectName));
|
|
51
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { usePanelStore } from "@/stores/panel-store";
|
|
2
|
+
import { basename } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Open a shell in the bottom dock that starts in `dir` (absolute host path).
|
|
6
|
+
*
|
|
7
|
+
* Used by "Open in Terminal" in the OS explorer and the project tree. The dock
|
|
8
|
+
* reveals itself and the tab is titled after the folder so several folder
|
|
9
|
+
* terminals stay distinguishable. `projectName` keeps the tab scoped to the
|
|
10
|
+
* project when the folder belongs to one; explorer folders outside any project
|
|
11
|
+
* pass none and the server only receives the start directory.
|
|
12
|
+
*/
|
|
13
|
+
export function openTerminalAt(dir: string, projectName?: string | null): string {
|
|
14
|
+
const title = basename(dir) || dir;
|
|
15
|
+
return usePanelStore.getState().openInDock({
|
|
16
|
+
type: "terminal",
|
|
17
|
+
title,
|
|
18
|
+
projectId: projectName ?? null,
|
|
19
|
+
closable: true,
|
|
20
|
+
metadata: { cwd: dir, ...(projectName ? { projectName } : {}) },
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { usePanelStore } from "@/stores/panel-store";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Deliver a shell command to a terminal.
|
|
5
|
+
*
|
|
6
|
+
* Two delivery paths, because a shell that is still booting cannot accept input:
|
|
7
|
+
* 1. A terminal already on screen answers the event synchronously and types the
|
|
8
|
+
* command right away — its shell is long past the prompt.
|
|
9
|
+
* 2. Nobody answers ⇒ open a terminal in the dock and hand the command over via
|
|
10
|
+
* `metadata.pendingCommand`. The tab holds it until its shell has printed a
|
|
11
|
+
* prompt (see `use-terminal-command-queue`), so the text is never swallowed
|
|
12
|
+
* by shell startup.
|
|
13
|
+
*/
|
|
14
|
+
export const RUN_IN_TERMINAL_EVENT = "ppm:run-in-terminal";
|
|
15
|
+
export const RUN_IN_TERMINAL_ACK_EVENT = "ppm:run-in-terminal:ack";
|
|
16
|
+
|
|
17
|
+
export interface RunInTerminalDetail {
|
|
18
|
+
command: string;
|
|
19
|
+
projectName?: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Normalize a copied code block into terminal input: drop `$ ` prompt markers and
|
|
24
|
+
* the trailing newline, so the last line waits on the user's Enter instead of
|
|
25
|
+
* running itself.
|
|
26
|
+
*/
|
|
27
|
+
export function normalizeTerminalCommand(text: string): string {
|
|
28
|
+
// A space after the `$` is required: `$HOME/bin/tool` starting a line is part of
|
|
29
|
+
// the command, not a prompt.
|
|
30
|
+
return text.replace(/^[ \t]*\$[ \t]+/gm, "").replace(/\s+$/, "");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function runInTerminal(rawCommand: string, projectName?: string | null): void {
|
|
34
|
+
const command = normalizeTerminalCommand(rawCommand);
|
|
35
|
+
if (!command) return;
|
|
36
|
+
|
|
37
|
+
// Offer it to a live terminal first (dispatch is synchronous, so the ack lands
|
|
38
|
+
// before this returns).
|
|
39
|
+
let handled = false;
|
|
40
|
+
const onAck = () => { handled = true; };
|
|
41
|
+
window.addEventListener(RUN_IN_TERMINAL_ACK_EVENT, onAck);
|
|
42
|
+
window.dispatchEvent(
|
|
43
|
+
new CustomEvent<RunInTerminalDetail>(RUN_IN_TERMINAL_EVENT, { detail: { command, projectName } }),
|
|
44
|
+
);
|
|
45
|
+
window.removeEventListener(RUN_IN_TERMINAL_ACK_EVENT, onAck);
|
|
46
|
+
if (handled) return;
|
|
47
|
+
|
|
48
|
+
usePanelStore.getState().openInDock({
|
|
49
|
+
type: "terminal",
|
|
50
|
+
title: "Terminal",
|
|
51
|
+
projectId: projectName ?? null,
|
|
52
|
+
closable: true,
|
|
53
|
+
metadata: { ...(projectName ? { projectName } : {}), pendingCommand: command },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { usePanelStore } from "@/stores/panel-store";
|
|
2
|
+
import type { Tab } from "@/stores/tab-store";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Deliver text (terminal output, and anything else worth quoting) to ONE chat:
|
|
6
|
+
* the one the user last selected.
|
|
7
|
+
*
|
|
8
|
+
* The event is addressed with `targetTabId` because every chat tab stays mounted
|
|
9
|
+
* — an unaddressed event is answered by all of them at once, so the same output
|
|
10
|
+
* lands as an attachment in every open chat.
|
|
11
|
+
*/
|
|
12
|
+
export const SEND_TO_CHAT_EVENT = "ppm:send-to-chat";
|
|
13
|
+
export const SEND_TO_CHAT_ACK_EVENT = "ppm:send-to-chat:ack";
|
|
14
|
+
|
|
15
|
+
export interface SendToChatDetail {
|
|
16
|
+
text: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
projectName?: string | null;
|
|
19
|
+
/** Only the chat tab with this id may consume the event. */
|
|
20
|
+
targetTabId?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Wall-clock activation stamp written by the panel store on every tab activation. */
|
|
24
|
+
function lastActiveAt(tab: Tab): number {
|
|
25
|
+
return typeof tab.metadata?.lastActiveAt === "number" ? (tab.metadata.lastActiveAt as number) : -1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The chat tab the user last selected.
|
|
30
|
+
*
|
|
31
|
+
* Ranked, highest first:
|
|
32
|
+
* 1. selected AND in the focused panel — with a split, the chat being worked in.
|
|
33
|
+
* 2. selected — its panel's active tab, so it is a chat actually on screen. The
|
|
34
|
+
* activation stamp alone is not enough: a chat opened programmatically carries
|
|
35
|
+
* a fresh stamp the user never chose, and a chat left behind another tab in its
|
|
36
|
+
* panel keeps its stamp long after it stopped being the one in view.
|
|
37
|
+
* 3. newest activation — the only ordering that compares across panels, and the
|
|
38
|
+
* answer when no chat is on screen at all.
|
|
39
|
+
*
|
|
40
|
+
* Chats of the given project always win over other projects' chats.
|
|
41
|
+
*/
|
|
42
|
+
export function resolveSelectedChatTabId(projectName?: string | null): string | null {
|
|
43
|
+
const { panels, focusedPanelId } = usePanelStore.getState();
|
|
44
|
+
|
|
45
|
+
const chats = Object.values(panels).flatMap((p) =>
|
|
46
|
+
p.tabs
|
|
47
|
+
.filter((t) => t.type === "chat")
|
|
48
|
+
.map((t) => ({
|
|
49
|
+
tab: t,
|
|
50
|
+
selected: p.activeTabId === t.id,
|
|
51
|
+
focusedPanel: p.id === focusedPanelId,
|
|
52
|
+
})),
|
|
53
|
+
);
|
|
54
|
+
if (chats.length === 0) return null;
|
|
55
|
+
|
|
56
|
+
const sameProject = projectName
|
|
57
|
+
? chats.filter((c) => c.tab.projectId === projectName || c.tab.metadata?.projectName === projectName)
|
|
58
|
+
: [];
|
|
59
|
+
const pool = sameProject.length > 0 ? sameProject : chats;
|
|
60
|
+
|
|
61
|
+
// Panel focus only separates chats that are BOTH on screen. For chats nobody has
|
|
62
|
+
// in view, the panel it happens to live in says nothing about which one the user
|
|
63
|
+
// picked last — only the stamp does.
|
|
64
|
+
const rank = (c: (typeof pool)[number]): [number, number] => [
|
|
65
|
+
c.selected ? (c.focusedPanel ? 2 : 1) : 0,
|
|
66
|
+
lastActiveAt(c.tab),
|
|
67
|
+
];
|
|
68
|
+
const best = pool.reduce((a, b) => {
|
|
69
|
+
const [ra, rb] = [rank(a), rank(b)];
|
|
70
|
+
for (let i = 0; i < ra.length; i++) {
|
|
71
|
+
if (ra[i]! !== rb[i]!) return ra[i]! > rb[i]! ? a : b;
|
|
72
|
+
}
|
|
73
|
+
return a;
|
|
74
|
+
});
|
|
75
|
+
return best.tab.id;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Send `text` to the chat the user last selected, as an attachment chip, focusing
|
|
80
|
+
* that chat so the user sees it arrive.
|
|
81
|
+
*
|
|
82
|
+
* Falls back through: live tab answers the event → tab exists but is not mounted
|
|
83
|
+
* yet, so hand the text over as `pendingMessage` → no chat open at all, open one.
|
|
84
|
+
*/
|
|
85
|
+
export function sendToChat(opts: { text: string; label?: string; projectName?: string | null }): void {
|
|
86
|
+
const { text, label, projectName } = opts;
|
|
87
|
+
if (!text.trim()) return;
|
|
88
|
+
|
|
89
|
+
const targetTabId = resolveSelectedChatTabId(projectName);
|
|
90
|
+
const store = usePanelStore.getState();
|
|
91
|
+
|
|
92
|
+
if (targetTabId) {
|
|
93
|
+
store.setActiveTab(targetTabId);
|
|
94
|
+
|
|
95
|
+
let handled = false;
|
|
96
|
+
const onAck = () => { handled = true; };
|
|
97
|
+
window.addEventListener(SEND_TO_CHAT_ACK_EVENT, onAck);
|
|
98
|
+
window.dispatchEvent(
|
|
99
|
+
new CustomEvent<SendToChatDetail>(SEND_TO_CHAT_EVENT, { detail: { text, label, projectName, targetTabId } }),
|
|
100
|
+
);
|
|
101
|
+
window.removeEventListener(SEND_TO_CHAT_ACK_EVENT, onAck);
|
|
102
|
+
if (handled) return;
|
|
103
|
+
|
|
104
|
+
// Lazy-mounted tab: it has no listener yet, so leave the text in its metadata
|
|
105
|
+
// for the composer to pick up on mount.
|
|
106
|
+
const tab = Object.values(store.panels).flatMap((p) => p.tabs).find((t) => t.id === targetTabId);
|
|
107
|
+
store.updateTab(targetTabId, { metadata: { ...tab?.metadata, pendingMessage: text } });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
store.openTab({
|
|
112
|
+
type: "chat",
|
|
113
|
+
title: "Chat",
|
|
114
|
+
projectId: null,
|
|
115
|
+
metadata: { ...(projectName ? { projectName } : {}), pendingMessage: text },
|
|
116
|
+
closable: true,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getAuthToken } from "@/lib/api-client";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Append the session token to a `/ws/...` path so the server can authenticate
|
|
5
|
+
* the upgrade. Browsers cannot set headers on WebSocket handshakes, so every
|
|
6
|
+
* socket (global, extensions, terminal, chat, group) carries it as a query
|
|
7
|
+
* parameter — one helper keeps the spelling identical to what the server reads.
|
|
8
|
+
*/
|
|
9
|
+
export function withWsAuth(path: string): string {
|
|
10
|
+
const token = getAuthToken();
|
|
11
|
+
if (!token || /[?&]token=/.test(path)) return path;
|
|
12
|
+
return `${path}${path.includes("?") ? "&" : "?"}token=${encodeURIComponent(token)}`;
|
|
13
|
+
}
|
package/src/web/lib/ws-client.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { withWsAuth } from "@/lib/ws-auth";
|
|
2
|
+
|
|
1
3
|
type MessageHandler = (data: MessageEvent) => void;
|
|
2
4
|
|
|
3
5
|
const MAX_RECONNECT_DELAY = 30_000;
|
|
@@ -33,17 +35,20 @@ export class WsClient {
|
|
|
33
35
|
this.cleanup();
|
|
34
36
|
|
|
35
37
|
const protocol = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
38
|
+
// Every app socket is authenticated at upgrade time; the token is read fresh
|
|
39
|
+
// on each (re)connect so a re-login mid-session is picked up.
|
|
40
|
+
const path = this.url.startsWith("ws") ? this.url : withWsAuth(this.url);
|
|
36
41
|
let fullUrl: string;
|
|
37
|
-
if (
|
|
38
|
-
fullUrl =
|
|
39
|
-
} else if (import.meta.env.DEV &&
|
|
42
|
+
if (path.startsWith("ws")) {
|
|
43
|
+
fullUrl = path;
|
|
44
|
+
} else if (import.meta.env.DEV && path.startsWith("/ws/") && window.location.protocol !== "https:") {
|
|
40
45
|
// Local dev over http: connect directly to backend (port 8081) to bypass
|
|
41
46
|
// Vite's dev proxy which has unreliable WebSocket upgrade handling.
|
|
42
47
|
// Over https (e.g. a Cloudflare tunnel) port 8081 isn't reachable and ws://
|
|
43
48
|
// is blocked as mixed content, so fall through to same-origin wss:// proxy.
|
|
44
|
-
fullUrl = `ws://${window.location.hostname}:8081${
|
|
49
|
+
fullUrl = `ws://${window.location.hostname}:8081${path}`;
|
|
45
50
|
} else {
|
|
46
|
-
fullUrl = `${protocol}//${window.location.host}${
|
|
51
|
+
fullUrl = `${protocol}//${window.location.host}${path}`;
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
this.ws = new WebSocket(fullUrl);
|
package/.release-run.sh
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./vendor-markdown-Ls5AcU91.js";import"./vendor-ui-CaeeFO0B.js";import{i as e,n as t}from"./api-client-rTAGdEKo.js";export{t as api,e as projectUrl};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./vendor-markdown-Ls5AcU91.js";import"./vendor-ui-CaeeFO0B.js";import"./api-client-rTAGdEKo.js";import{v as e}from"./api-settings-CAhcNpj5.js";export{e as updateDeviceName};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{K as e}from"./vendor-mermaid-C977xNeE.js";export{e as createArchitectureServices};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{x as e}from"./vendor-markdown-Ls5AcU91.js";import"./vendor-ui-CaeeFO0B.js";import{t}from"./createLucideIcon-D8pyde1A.js";import{t as n}from"./file-exclamation-point-COAwgNGw.js";import"./api-client-rTAGdEKo.js";import{t as r}from"./utils-B9H0d3Fh.js";import{Hn as i}from"./index-wd3FSHHn.js";import{t as a}from"./use-blob-url-DYtOHK7u.js";var o=t(`music`,[[`path`,{d:`M9 18V5l12-2v13`,key:`1jmyc2`}],[`circle`,{cx:`6`,cy:`18`,r:`3`,key:`fqmcym`}],[`circle`,{cx:`18`,cy:`16`,r:`3`,key:`1hluhg`}]]),s=e();function c({filePath:e,projectName:t}){let{blobUrl:c,error:l}=a(e,t);return l?(0,s.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,s.jsx)(n,{className:`size-10 text-text-subtle`}),(0,s.jsx)(`p`,{className:`text-sm`,children:`Failed to load audio.`})]}):c?(0,s.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-4 p-4 bg-surface`,children:[(0,s.jsx)(o,{className:`size-16 text-text-subtle`}),(0,s.jsx)(`p`,{className:`text-sm text-text-secondary truncate max-w-xs`,children:r(e)}),(0,s.jsx)(`audio`,{src:c,controls:!0,className:`w-full max-w-md`})]}):(0,s.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,s.jsx)(i,{className:`size-5 animate-spin text-text-subtle`})})}export{c as AudioPreview};
|