@hienlh/ppm 0.18.2 → 0.18.3
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 +17 -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-3Zk3gYSZ.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-DvbbVMBi.js +15 -0
- package/dist/web/assets/{cobol-Q6YgysnI.js → cobol-Byn9FYKu.js} +1 -1
- package/dist/web/assets/code-editor-BKg7Li-L.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-CJNtovCh.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-BR6gCSZJ.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-DzuwuFSr.js} +1 -1
- package/dist/web/assets/diff-viewer-DL3_tISz.js +4 -0
- package/dist/web/assets/{docx-preview-Dd2nTW2v.js → docx-preview-BzgyssBV.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-CrrY1HbH.js +2 -0
- package/dist/web/assets/explorer-window-content-CvQPeiwU.js +1 -0
- package/dist/web/assets/{extension-webview-Dk_zyoXZ.js → extension-webview-DH1PfQF2.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-gUm4E3wo.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-D8J5lwUP.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-DihiQPSi.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-DOQBNgwl.js} +1 -1
- package/dist/web/assets/index-BtncTUyM.js +52 -0
- package/dist/web/assets/index-Bucbpby-.css +2 -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-AynYQVnV.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-BBlGpszb.js +1 -0
- package/dist/web/assets/{markdown-renderer-B4uL6GVA.js → markdown-renderer-CVGub2iS.js} +3 -3
- 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-CSgc5JOk.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-1ksB0YwR.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-DSAkWbxY.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-DOiM_5Nv.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-D_oruJB9.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-PWpuw-O5.js +1 -0
- package/dist/web/assets/{sqlite-viewer-CZYDknNa.js → sqlite-viewer-1S6cwVWa.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-U0Jh9-2v.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-DSZL4GIj.js +1 -0
- package/dist/web/assets/{templ-DbG2y5yD.js → templ-Dv8zpKZW.js} +1 -1
- package/dist/web/assets/terminal-tab-uVhX8AGG.js +3 -0
- package/dist/web/assets/{tex-CeKQhrQG.js → tex-CsrOiqh1.js} +1 -1
- package/dist/web/assets/tool-cards-QuTCNfYR.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-DvmINdhI.js +1 -0
- package/dist/web/assets/use-websocket-DoZdQS0G.js +1 -0
- package/dist/web/assets/{vendor-mermaid-C977xNeE.js → vendor-mermaid-Bg4vE8QA.js} +1 -1
- package/dist/web/assets/video-preview-CzU09X0C.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 +44 -18
- 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,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turns an OS `Files` drop (or a picked `FileList`) into a flat list of `{file, relativePath}`
|
|
3
|
+
* pairs, walking nested folders when the browser exposes `webkitGetAsEntry`. Falls back to a
|
|
4
|
+
* flat `dataTransfer.files` list — no relative path beyond the bare name — on a browser that
|
|
5
|
+
* never implemented the entries API.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface DroppedEntry {
|
|
9
|
+
file: File;
|
|
10
|
+
/** Forward-slash path relative to the drop target, including the file's own name. A flat
|
|
11
|
+
* drop (no folder) is just the bare file name. */
|
|
12
|
+
relativePath: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Reject directory traversal and normalise separators. A dropped folder's real
|
|
17
|
+
* `FileSystemEntry.fullPath` never contains `..`, but a rebuilt/spoofed `DataTransfer` (or a
|
|
18
|
+
* test) might, and this is the one seam every path this module produces passes through.
|
|
19
|
+
* Returns `null` when nothing safe is left (empty, or only `..`/`.` segments).
|
|
20
|
+
*/
|
|
21
|
+
export function sanitizeRelativePath(raw: string): string | null {
|
|
22
|
+
const unified = raw.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
23
|
+
const segments = unified.split("/").filter((segment) => segment.length > 0 && segment !== ".");
|
|
24
|
+
if (segments.length === 0 || segments.some((segment) => segment === "..")) return null;
|
|
25
|
+
return segments.join("/");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function readEntryAsFile(entry: FileSystemFileEntry): Promise<File> {
|
|
29
|
+
return new Promise((resolve, reject) => entry.file(resolve, reject));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `readEntries` only ever returns one batch per call — the empty batch that signals the end
|
|
33
|
+
* is itself the loop's exit condition, not a sentinel value. */
|
|
34
|
+
function readAllEntries(reader: FileSystemDirectoryReader): Promise<FileSystemEntry[]> {
|
|
35
|
+
return new Promise((resolve, reject) => {
|
|
36
|
+
const all: FileSystemEntry[] = [];
|
|
37
|
+
const readNextBatch = () => {
|
|
38
|
+
reader.readEntries((batch) => {
|
|
39
|
+
if (batch.length === 0) { resolve(all); return; }
|
|
40
|
+
all.push(...batch);
|
|
41
|
+
readNextBatch();
|
|
42
|
+
}, reject);
|
|
43
|
+
};
|
|
44
|
+
readNextBatch();
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function walk(entry: FileSystemEntry, out: DroppedEntry[]): Promise<void> {
|
|
49
|
+
if (entry.isFile) {
|
|
50
|
+
const relativePath = sanitizeRelativePath(entry.fullPath);
|
|
51
|
+
if (!relativePath) return;
|
|
52
|
+
out.push({ file: await readEntryAsFile(entry as FileSystemFileEntry), relativePath });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (entry.isDirectory) {
|
|
56
|
+
const reader = (entry as FileSystemDirectoryEntry).createReader();
|
|
57
|
+
for (const child of await readAllEntries(reader)) await walk(child, out);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Entry point: prefers the entries API (nested folders survive), falls back to a flat list. */
|
|
62
|
+
export async function collectDroppedEntries(dataTransfer: DataTransfer): Promise<DroppedEntry[]> {
|
|
63
|
+
const entries = Array.from(dataTransfer.items ?? [])
|
|
64
|
+
.filter((item) => item.kind === "file" && typeof item.webkitGetAsEntry === "function")
|
|
65
|
+
.map((item) => item.webkitGetAsEntry())
|
|
66
|
+
.filter((entry): entry is FileSystemEntry => entry != null);
|
|
67
|
+
|
|
68
|
+
if (entries.length > 0) {
|
|
69
|
+
const out: DroppedEntry[] = [];
|
|
70
|
+
for (const entry of entries) await walk(entry, out);
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// No entries API (or every item resolved to null) — flat files only, no folder structure.
|
|
75
|
+
const flat: DroppedEntry[] = [];
|
|
76
|
+
for (const file of Array.from(dataTransfer.files)) {
|
|
77
|
+
const relativePath = sanitizeRelativePath(file.name);
|
|
78
|
+
if (relativePath) flat.push({ file, relativePath });
|
|
79
|
+
}
|
|
80
|
+
return flat;
|
|
81
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded-concurrency runner for a batch of uploads, aggregating byte-level progress across
|
|
3
|
+
* every in-flight job (not just completed ones) so one large file streaming for a while does
|
|
4
|
+
* not make the overall percentage look stuck.
|
|
5
|
+
*
|
|
6
|
+
* Pure and DOM-free on purpose — `job.run` is the only place XHR/fetch appears, so this file
|
|
7
|
+
* is unit-testable without a browser.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface UploadJob<T> {
|
|
11
|
+
id: string;
|
|
12
|
+
/** Byte size, for weighting this job's share of the aggregate progress. */
|
|
13
|
+
size: number;
|
|
14
|
+
run(onProgress: (bytesLoaded: number) => void): Promise<T>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface UploadQueueProgress {
|
|
18
|
+
completed: number;
|
|
19
|
+
total: number;
|
|
20
|
+
bytesLoaded: number;
|
|
21
|
+
bytesTotal: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface UploadQueueResult<T> {
|
|
25
|
+
id: string;
|
|
26
|
+
ok: boolean;
|
|
27
|
+
value?: T;
|
|
28
|
+
error?: unknown;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Runs `jobs` with at most `concurrency` in flight at once. Results are returned in the same
|
|
33
|
+
* order as `jobs`, regardless of which finished first — callers that report per-file outcomes
|
|
34
|
+
* need that order to line up with what they queued.
|
|
35
|
+
*/
|
|
36
|
+
export async function runUploadQueue<T>(
|
|
37
|
+
jobs: UploadJob<T>[],
|
|
38
|
+
concurrency: number,
|
|
39
|
+
onProgress: (progress: UploadQueueProgress) => void,
|
|
40
|
+
): Promise<UploadQueueResult<T>[]> {
|
|
41
|
+
const bytesTotal = jobs.reduce((sum, job) => sum + job.size, 0);
|
|
42
|
+
const loaded = new Array<number>(jobs.length).fill(0);
|
|
43
|
+
const results = new Array<UploadQueueResult<T>>(jobs.length);
|
|
44
|
+
let completed = 0;
|
|
45
|
+
|
|
46
|
+
const report = () => {
|
|
47
|
+
onProgress({
|
|
48
|
+
completed,
|
|
49
|
+
total: jobs.length,
|
|
50
|
+
bytesLoaded: loaded.reduce((sum, n) => sum + n, 0),
|
|
51
|
+
bytesTotal,
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
let nextIndex = 0;
|
|
56
|
+
async function worker(): Promise<void> {
|
|
57
|
+
for (;;) {
|
|
58
|
+
const index = nextIndex++;
|
|
59
|
+
if (index >= jobs.length) return;
|
|
60
|
+
const job = jobs[index]!;
|
|
61
|
+
try {
|
|
62
|
+
const value = await job.run((bytesLoaded) => {
|
|
63
|
+
loaded[index] = bytesLoaded;
|
|
64
|
+
report();
|
|
65
|
+
});
|
|
66
|
+
results[index] = { id: job.id, ok: true, value };
|
|
67
|
+
} catch (error) {
|
|
68
|
+
results[index] = { id: job.id, ok: false, error };
|
|
69
|
+
}
|
|
70
|
+
loaded[index] = job.size;
|
|
71
|
+
completed++;
|
|
72
|
+
report();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const workerCount = Math.max(1, Math.min(concurrency, jobs.length));
|
|
77
|
+
await Promise.all(Array.from({ length: workerCount }, worker));
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backs the toolbar/context-menu/mobile "Upload…" actions: a hidden native file input the
|
|
3
|
+
* button clicks programmatically, so the browser's own picker chrome is used instead of a
|
|
4
|
+
* custom one. A second hidden input with the non-standard `webkitdirectory` attribute gives
|
|
5
|
+
* folder uploads on the browsers that support it (Chromium, Safari) — Firefox never
|
|
6
|
+
* implemented it, and `supportsFolderPicker` is what lets a caller hide that trigger there
|
|
7
|
+
* instead of offering a picker that silently behaves like a flat file picker.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { useRef, type ChangeEvent, type ReactNode } from "react";
|
|
11
|
+
import type { DroppedEntry } from "./collect-dropped-entries";
|
|
12
|
+
|
|
13
|
+
export interface UploadPicker {
|
|
14
|
+
supportsFolderPicker: boolean;
|
|
15
|
+
openFilePicker(): void;
|
|
16
|
+
openFolderPicker(): void;
|
|
17
|
+
/** Render this once anywhere in the tree — the inputs are visually hidden. */
|
|
18
|
+
inputs: ReactNode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Non-standard but present on Chromium/WebKit's `<input>`; absent from the DOM lib types. */
|
|
22
|
+
interface DirectoryPickerInputProps {
|
|
23
|
+
webkitdirectory?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function useUploadPicker(onFiles: (entries: DroppedEntry[]) => void): UploadPicker {
|
|
27
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
28
|
+
const folderInputRef = useRef<HTMLInputElement>(null);
|
|
29
|
+
const supportsFolderPicker =
|
|
30
|
+
typeof document !== "undefined" && "webkitdirectory" in document.createElement("input");
|
|
31
|
+
|
|
32
|
+
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
|
33
|
+
const files = e.target.files;
|
|
34
|
+
// Clears the value so picking the exact same file/folder again still fires `onChange`.
|
|
35
|
+
e.target.value = "";
|
|
36
|
+
if (!files || files.length === 0) return;
|
|
37
|
+
const entries: DroppedEntry[] = Array.from(files).map((file) => ({
|
|
38
|
+
file,
|
|
39
|
+
// The folder picker sets `webkitRelativePath` ("folder/sub/file.txt"); a plain file
|
|
40
|
+
// picker leaves it empty, so the bare name is the relative path.
|
|
41
|
+
relativePath: (file as File & { webkitRelativePath?: string }).webkitRelativePath || file.name,
|
|
42
|
+
}));
|
|
43
|
+
onFiles(entries);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
supportsFolderPicker,
|
|
48
|
+
openFilePicker: () => fileInputRef.current?.click(),
|
|
49
|
+
openFolderPicker: () => folderInputRef.current?.click(),
|
|
50
|
+
inputs: (
|
|
51
|
+
<>
|
|
52
|
+
<input
|
|
53
|
+
ref={fileInputRef}
|
|
54
|
+
type="file"
|
|
55
|
+
multiple
|
|
56
|
+
hidden
|
|
57
|
+
onChange={handleChange}
|
|
58
|
+
data-testid="explorer-upload-file-input"
|
|
59
|
+
/>
|
|
60
|
+
{supportsFolderPicker && (
|
|
61
|
+
<input
|
|
62
|
+
ref={folderInputRef}
|
|
63
|
+
{...({ webkitdirectory: "" } satisfies DirectoryPickerInputProps)}
|
|
64
|
+
type="file"
|
|
65
|
+
multiple
|
|
66
|
+
hidden
|
|
67
|
+
onChange={handleChange}
|
|
68
|
+
data-testid="explorer-upload-folder-input"
|
|
69
|
+
/>
|
|
70
|
+
)}
|
|
71
|
+
</>
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
@@ -36,7 +36,9 @@ export function ColumnRow({ entry, selected, currentDir, hasClipboard, isPinned,
|
|
|
36
36
|
// Column view has no multi-select — a drag always carries just this one entry.
|
|
37
37
|
// Spring-load is Column view's own drill-in gesture (`onSelect` already navigates for a
|
|
38
38
|
// directory), so a rested hover reaches a deep folder without dropping first.
|
|
39
|
-
const dnd = useEntryRowDnd({
|
|
39
|
+
const dnd = useEntryRowDnd({
|
|
40
|
+
entry, dragPaths: [entry.path], run: actions.transferInto, springLoad: onSelect, onFiles: actions.uploadInto,
|
|
41
|
+
});
|
|
40
42
|
return (
|
|
41
43
|
<ContextMenu>
|
|
42
44
|
<ContextMenuTrigger asChild>
|
|
@@ -61,7 +61,12 @@ export function ColumnViewColumn({
|
|
|
61
61
|
// The column's own empty area is a drop target for its own directory — dropping below the
|
|
62
62
|
// last row of an ancestor column moves/copies into that ancestor, not just the deepest one.
|
|
63
63
|
// Mobile's single full-width column never gets one: no drag source can ever start on touch.
|
|
64
|
-
const backgroundDrop = usePathDropTarget({
|
|
64
|
+
const backgroundDrop = usePathDropTarget({
|
|
65
|
+
targetDir: path,
|
|
66
|
+
run: actions.transferInto,
|
|
67
|
+
onFiles: actions.uploadInto,
|
|
68
|
+
disabled: fullWidth,
|
|
69
|
+
});
|
|
65
70
|
|
|
66
71
|
return (
|
|
67
72
|
<div
|
|
@@ -42,7 +42,7 @@ export const IconsViewTile = memo(function IconsViewTile(props: IconsViewTilePro
|
|
|
42
42
|
// A drag carries whatever a context menu would act on: the selection when this tile is
|
|
43
43
|
// part of it, otherwise just this tile.
|
|
44
44
|
const dragPaths = useMemo(() => menuTargets.map((target) => target.path), [menuTargets]);
|
|
45
|
-
const dnd = useEntryRowDnd({ entry, dragPaths, run: actions.transferInto });
|
|
45
|
+
const dnd = useEntryRowDnd({ entry, dragPaths, run: actions.transferInto, onFiles: actions.uploadInto });
|
|
46
46
|
return (
|
|
47
47
|
<ContextMenu>
|
|
48
48
|
<ContextMenuTrigger asChild>
|
|
@@ -76,7 +76,11 @@ export function IconsView({
|
|
|
76
76
|
const creating = slice.inlineEdit?.kind === "new-file" || slice.inlineEdit?.kind === "new-folder";
|
|
77
77
|
// The view background is the current directory itself — a tile already claims the drop
|
|
78
78
|
// (and stops the event) when it is a directory, so this only ever fires over empty space.
|
|
79
|
-
const backgroundDrop = usePathDropTarget({
|
|
79
|
+
const backgroundDrop = usePathDropTarget({
|
|
80
|
+
targetDir: slice.path,
|
|
81
|
+
run: actions.transferInto,
|
|
82
|
+
onFiles: actions.uploadInto,
|
|
83
|
+
});
|
|
80
84
|
|
|
81
85
|
const onGridKeyDown = (event: KeyboardEvent) => {
|
|
82
86
|
if (event.altKey || event.ctrlKey || event.metaKey) return;
|
|
@@ -44,7 +44,7 @@ export const ListRow = memo(function ListRow(props: ListRowProps) {
|
|
|
44
44
|
// A drag carries whatever a context menu would act on: the selection when this row is part
|
|
45
45
|
// of it, otherwise just this row.
|
|
46
46
|
const dragPaths = useMemo(() => menuTargets.map((target) => target.path), [menuTargets]);
|
|
47
|
-
const dnd = useEntryRowDnd({ entry, dragPaths, run: actions.transferInto });
|
|
47
|
+
const dnd = useEntryRowDnd({ entry, dragPaths, run: actions.transferInto, onFiles: actions.uploadInto });
|
|
48
48
|
return (
|
|
49
49
|
<ContextMenu>
|
|
50
50
|
<ContextMenuTrigger asChild>
|
|
@@ -62,7 +62,11 @@ export function ListView({
|
|
|
62
62
|
const creating = slice.inlineEdit?.kind === "new-file" || slice.inlineEdit?.kind === "new-folder";
|
|
63
63
|
// The view background is the current directory itself — a row already claims the drop
|
|
64
64
|
// (and stops the event) when it is a directory, so this only ever fires over empty space.
|
|
65
|
-
const backgroundDrop = usePathDropTarget({
|
|
65
|
+
const backgroundDrop = usePathDropTarget({
|
|
66
|
+
targetDir: slice.path,
|
|
67
|
+
run: actions.transferInto,
|
|
68
|
+
onFiles: actions.uploadInto,
|
|
69
|
+
});
|
|
66
70
|
|
|
67
71
|
const toggleSort = (key: SortKey) =>
|
|
68
72
|
setPrefs({ sort: { key, dir: sort.key === key && sort.dir === "asc" ? "desc" : "asc" } });
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useState, useEffect, useRef, type ReactNode } from "react";
|
|
2
2
|
import { useMdContext, FILE_EXT_RE, GLOB_CHARS_RE } from "./markdown-context";
|
|
3
|
-
import { useTabStore } from "@/stores/tab-store";
|
|
4
3
|
import { highlightToHtml, highlightSync, getActiveShikiTheme } from "@/theme/adapters/shiki-adapter";
|
|
5
4
|
import { copyToClipboard } from "@/lib/clipboard";
|
|
5
|
+
import { runInTerminal } from "@/lib/run-in-terminal";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Shiki-highlighted HTML for a code block. Returns null while streaming or
|
|
@@ -64,7 +64,6 @@ function hastToText(node: any): string {
|
|
|
64
64
|
/** Pre — code block wrapper with mermaid detection, Shiki highlighting, action buttons */
|
|
65
65
|
export function MdPre({ children, node, ...rest }: any) {
|
|
66
66
|
const { codeActions, projectName, isStreaming } = useMdContext();
|
|
67
|
-
const openTab = useTabStore((s) => s.openTab);
|
|
68
67
|
|
|
69
68
|
const codeNode = node?.children?.[0];
|
|
70
69
|
const langClass = (codeNode?.properties?.className ?? []).find((c: string) => c.startsWith("language-"));
|
|
@@ -92,12 +91,9 @@ export function MdPre({ children, node, ...rest }: any) {
|
|
|
92
91
|
<ActionBtn title="Copy" icon={<CopyIcon />} activeIcon={<CheckIcon />} onClick={() => void copyToClipboard(text)} />
|
|
93
92
|
{isBash && projectName && (
|
|
94
93
|
<ActionBtn
|
|
95
|
-
title="
|
|
94
|
+
title="Send to terminal"
|
|
96
95
|
icon={<PlayIcon />}
|
|
97
|
-
onClick={() =>
|
|
98
|
-
void copyToClipboard(text.replace(/^\$\s*/gm, ""));
|
|
99
|
-
openTab({ type: "terminal", title: "Terminal", metadata: { projectName }, projectId: projectName, closable: true });
|
|
100
|
-
}}
|
|
96
|
+
onClick={() => runInTerminal(text, projectName)}
|
|
101
97
|
/>
|
|
102
98
|
)}
|
|
103
99
|
</div>
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { useRef, useEffect, useState, useCallback, memo } from "react";
|
|
2
2
|
import { useTerminal } from "@/hooks/use-terminal";
|
|
3
3
|
import { useTerminalTouchSelection } from "@/hooks/use-terminal-touch-selection";
|
|
4
|
+
import { useTerminalCommandQueue } from "@/hooks/use-terminal-command-queue";
|
|
5
|
+
import { sendToChat } from "@/lib/send-to-chat";
|
|
4
6
|
import { cn } from "@/lib/utils";
|
|
5
7
|
import { copyToClipboard } from "@/lib/clipboard";
|
|
6
8
|
import { RotateCcw, MessageSquare } from "lucide-react";
|
|
7
9
|
import "@xterm/xterm/css/xterm.css";
|
|
8
10
|
import { toast } from "sonner";
|
|
9
11
|
|
|
10
|
-
import { usePanelStore } from "@/stores/panel-store";
|
|
11
12
|
import { TerminalMobileToolbar } from "./terminal-mobile-toolbar";
|
|
12
13
|
import { TerminalLinksSheet } from "./terminal-links-sheet";
|
|
13
14
|
|
|
@@ -19,9 +20,10 @@ interface TerminalTabProps {
|
|
|
19
20
|
export const TerminalTab = memo(function TerminalTab({ metadata, tabId }: TerminalTabProps) {
|
|
20
21
|
const sessionId = (metadata?.sessionId as string) ?? "new";
|
|
21
22
|
const projectName = metadata?.projectName as string | undefined;
|
|
23
|
+
const cwd = metadata?.cwd as string | undefined;
|
|
22
24
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
23
25
|
|
|
24
|
-
const { connected, reconnecting, exited, sendData, getSelection, getLastCommandOutput, getBufferUrls, restart } = useTerminal({ sessionId, projectName, containerRef, tabId });
|
|
26
|
+
const { connected, reconnecting, exited, shellReady, sendData, getSelection, getLastCommandOutput, getLastCommand, getBufferUrls, restart } = useTerminal({ sessionId, projectName, cwd, containerRef, tabId });
|
|
25
27
|
const [ctrlMode, setCtrlMode] = useState(false);
|
|
26
28
|
const [selectMode, setSelectMode] = useState(false);
|
|
27
29
|
const [linksOpen, setLinksOpen] = useState(false);
|
|
@@ -35,6 +37,10 @@ export const TerminalTab = memo(function TerminalTab({ metadata, tabId }: Termin
|
|
|
35
37
|
termElement?.focus();
|
|
36
38
|
}, []);
|
|
37
39
|
|
|
40
|
+
// "Run in terminal" (chat code blocks) types its command in here, waiting for
|
|
41
|
+
// the shell to finish booting first.
|
|
42
|
+
useTerminalCommandQueue({ tabId, metadata, shellReady, sendData, focusTerminal, containerRef });
|
|
43
|
+
|
|
38
44
|
// Raising the soft keyboard needs focus() to run inside a gesture the browser
|
|
39
45
|
// recognises, and xterm's own mousedown handling does not reliably qualify.
|
|
40
46
|
// Capture-phase `touchend` runs before xterm's handlers and is unambiguously a
|
|
@@ -100,33 +106,21 @@ export const TerminalTab = memo(function TerminalTab({ metadata, tabId }: Termin
|
|
|
100
106
|
}
|
|
101
107
|
}, [sendData, focusTerminal]);
|
|
102
108
|
|
|
109
|
+
// Output without the command that produced it reads as an orphan in chat, so the
|
|
110
|
+
// prompt line goes along with it. A selection is sent verbatim — the user already
|
|
111
|
+
// picked exactly what they meant.
|
|
103
112
|
const handleSendToChat = useCallback(() => {
|
|
104
113
|
const selection = getSelection();
|
|
105
|
-
const
|
|
106
|
-
if (!
|
|
107
|
-
const
|
|
114
|
+
const body = selection || getLastCommandOutput();
|
|
115
|
+
if (!body.trim()) return;
|
|
116
|
+
const command = selection ? "" : getLastCommand();
|
|
117
|
+
const transcript = command ? `${command}\n${body}` : body;
|
|
118
|
+
const codeBlock = "```bash\n" + transcript + "\n```";
|
|
108
119
|
const label = selection ? "Terminal selection" : "Terminal output";
|
|
109
120
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
window.addEventListener("ppm:send-to-chat:ack", handler, { once: true });
|
|
114
|
-
window.dispatchEvent(new CustomEvent("ppm:send-to-chat", { detail: { text: codeBlock, label, projectName } }));
|
|
115
|
-
window.removeEventListener("ppm:send-to-chat:ack", handler);
|
|
116
|
-
|
|
117
|
-
if (handled) {
|
|
118
|
-
toast.success("Sent to chat", { duration: 1500 });
|
|
119
|
-
} else {
|
|
120
|
-
// No chat tab mounted — open a new one with the content as initialValue
|
|
121
|
-
usePanelStore.getState().openTab({
|
|
122
|
-
type: "chat",
|
|
123
|
-
title: "Chat",
|
|
124
|
-
projectId: null,
|
|
125
|
-
metadata: { projectName, pendingMessage: codeBlock },
|
|
126
|
-
closable: true,
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}, [getSelection, getLastCommandOutput, projectName]);
|
|
121
|
+
sendToChat({ text: codeBlock, label, projectName });
|
|
122
|
+
toast.success("Sent to chat", { duration: 1500 });
|
|
123
|
+
}, [getSelection, getLastCommandOutput, getLastCommand, projectName]);
|
|
130
124
|
|
|
131
125
|
const isMobile = typeof window !== "undefined" && "ontouchstart" in window;
|
|
132
126
|
|
|
@@ -7,6 +7,7 @@ import { usePanelStore } from "@/stores/panel-store";
|
|
|
7
7
|
import { playNotificationSound } from "@/lib/notification-sounds";
|
|
8
8
|
import { toast } from "sonner";
|
|
9
9
|
import type { ChatMessage, ChatEvent } from "../../types/chat";
|
|
10
|
+
import type { BackgroundAgentStatus } from "../../shared/background-agent-status";
|
|
10
11
|
import type { ChatWsServerMessage, SessionPhase, BackgroundShell, VersionGroup } from "../../types/api";
|
|
11
12
|
import { useBackgroundOutputStore } from "../stores/background-output-store";
|
|
12
13
|
|
|
@@ -210,6 +211,22 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
210
211
|
updateTeamActivity();
|
|
211
212
|
}, [updateTeamActivity]);
|
|
212
213
|
|
|
214
|
+
/** Merge a team's on-disk message history into the activity buffer. */
|
|
215
|
+
const loadTeamDetail = useCallback(async (teamName: string) => {
|
|
216
|
+
try {
|
|
217
|
+
const res = await api.get<any>(`/api/teams/${encodeURIComponent(teamName)}`);
|
|
218
|
+
if (!res?.messages) return;
|
|
219
|
+
const existing = teamActivityRef.current.messages;
|
|
220
|
+
const newMsgs = (res.messages as any[]).filter(
|
|
221
|
+
(m: any) => !existing.some((e) => e.timestamp === m.timestamp && e.from === m.from)
|
|
222
|
+
);
|
|
223
|
+
existing.push(...newMsgs);
|
|
224
|
+
existing.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
225
|
+
if (existing.length > 500) existing.splice(0, existing.length - 500);
|
|
226
|
+
updateTeamActivity();
|
|
227
|
+
} catch { /* team deleted or unreadable */ }
|
|
228
|
+
}, [updateTeamActivity]);
|
|
229
|
+
|
|
213
230
|
// Derived state
|
|
214
231
|
const isStreaming = phase !== "idle";
|
|
215
232
|
|
|
@@ -316,6 +333,37 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
316
333
|
}
|
|
317
334
|
}, [flushMessages]);
|
|
318
335
|
|
|
336
|
+
/**
|
|
337
|
+
* Stamp a backgrounded Agent/Task card with its terminal state.
|
|
338
|
+
*
|
|
339
|
+
* The card may live in either half of the transcript: the streaming buffer when the agent
|
|
340
|
+
* finishes inside the turn that spawned it, or a finalized message when it outlives that
|
|
341
|
+
* turn — which is the common case, and the one that used to leave a green check on a
|
|
342
|
+
* still-running agent.
|
|
343
|
+
*/
|
|
344
|
+
const markSubagentStatus = useCallback((toolUseId: string, status: BackgroundAgentStatus) => {
|
|
345
|
+
const isTarget = (e: ChatEvent) =>
|
|
346
|
+
e.type === "tool_use" && (e.tool === "Agent" || e.tool === "Task") && (e as any).toolUseId === toolUseId;
|
|
347
|
+
|
|
348
|
+
const idx = streamingEventsRef.current.findIndex(isTarget);
|
|
349
|
+
if (idx !== -1) {
|
|
350
|
+
streamingEventsRef.current[idx] = { ...streamingEventsRef.current[idx]!, bgStatus: status } as ChatEvent;
|
|
351
|
+
syncMessages();
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
setMessages((prev) => {
|
|
356
|
+
let mi = -1;
|
|
357
|
+
for (let i = prev.length - 1; i >= 0; i--) {
|
|
358
|
+
if (prev[i]!.events?.some(isTarget)) { mi = i; break; }
|
|
359
|
+
}
|
|
360
|
+
if (mi === -1) return prev;
|
|
361
|
+
const msg = prev[mi]!;
|
|
362
|
+
const events = msg.events!.map((e) => (isTarget(e) ? { ...e, bgStatus: status } as ChatEvent : e));
|
|
363
|
+
return [...prev.slice(0, mi), { ...msg, events }, ...prev.slice(mi + 1)];
|
|
364
|
+
});
|
|
365
|
+
}, [syncMessages]);
|
|
366
|
+
|
|
319
367
|
/** Process a single stream event — reused by live events and turn_events replay */
|
|
320
368
|
const processStreamEvent = useCallback((data: unknown) => {
|
|
321
369
|
const ev = data as any;
|
|
@@ -536,20 +584,8 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
536
584
|
const teamName = ev.teamName as string;
|
|
537
585
|
if (teamName) {
|
|
538
586
|
teamActivityRef.current.teamNames.add(teamName);
|
|
539
|
-
// Fetch full team data from REST
|
|
540
|
-
api.get<any>(`/api/teams/${encodeURIComponent(teamName)}`).then((res: any) => {
|
|
541
|
-
if (res?.messages) {
|
|
542
|
-
const existing = teamActivityRef.current.messages;
|
|
543
|
-
const newMsgs = (res.messages as any[]).filter(
|
|
544
|
-
(m: any) => !existing.some((e) => e.timestamp === m.timestamp && e.from === m.from)
|
|
545
|
-
);
|
|
546
|
-
existing.push(...newMsgs);
|
|
547
|
-
existing.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
548
|
-
if (existing.length > 500) existing.splice(0, existing.length - 500);
|
|
549
|
-
}
|
|
550
|
-
updateTeamActivity();
|
|
551
|
-
}).catch(() => {});
|
|
552
587
|
updateTeamActivity();
|
|
588
|
+
void loadTeamDetail(teamName);
|
|
553
589
|
}
|
|
554
590
|
break;
|
|
555
591
|
}
|
|
@@ -593,6 +629,15 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
593
629
|
break;
|
|
594
630
|
}
|
|
595
631
|
|
|
632
|
+
case "subagent_status": {
|
|
633
|
+
// A backgrounded Agent finished. Its card has shown a spinner since the launch ack
|
|
634
|
+
// (see isAsyncAgentLaunchAck) — stamp the terminal state so it can settle.
|
|
635
|
+
const tuId = ev.toolUseId as string | undefined;
|
|
636
|
+
const status = ev.status as BackgroundAgentStatus | undefined;
|
|
637
|
+
if (tuId && status) markSubagentStatus(tuId, status);
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
|
|
596
641
|
case "background_registry": {
|
|
597
642
|
const shells = (ev.shells as BackgroundShell[]) ?? [];
|
|
598
643
|
backgroundShellsRef.current = shells;
|
|
@@ -656,7 +701,7 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
656
701
|
break;
|
|
657
702
|
}
|
|
658
703
|
}
|
|
659
|
-
}, [routeToParent, routeToFinalizedParent, syncMessages]);
|
|
704
|
+
}, [routeToParent, routeToFinalizedParent, syncMessages, markSubagentStatus, updateTeamActivity, loadTeamDetail]);
|
|
660
705
|
|
|
661
706
|
const handleMessage = useCallback((event: MessageEvent) => {
|
|
662
707
|
let data: ChatWsServerMessage;
|
|
@@ -914,6 +959,23 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
914
959
|
setTeamActivity(EMPTY_TEAM_ACTIVITY);
|
|
915
960
|
setTeamMessages([]);
|
|
916
961
|
|
|
962
|
+
// Rehydrate the team from disk. team_detected only arrives live and its replay
|
|
963
|
+
// buffer is cleared at every turn boundary, so without this a reload — or any
|
|
964
|
+
// later turn — drops the team UI even while the team is still running.
|
|
965
|
+
if (sessionId) {
|
|
966
|
+
api.get<any[]>("/api/teams").then((teams) => {
|
|
967
|
+
if (cancelled || !Array.isArray(teams)) return;
|
|
968
|
+
// Only the team named after this session. The global list carries other
|
|
969
|
+
// sessions' teams and nothing on disk attributes those to a session, so
|
|
970
|
+
// matching by name is what keeps one session's team out of another's UI.
|
|
971
|
+
const mine = teams.filter((t: any) => (t?.name ?? t?.team_name) === sessionId);
|
|
972
|
+
if (mine.length === 0) return;
|
|
973
|
+
teamActivityRef.current.teamNames.add(sessionId);
|
|
974
|
+
updateTeamActivity();
|
|
975
|
+
void loadTeamDetail(sessionId);
|
|
976
|
+
}).catch(() => {});
|
|
977
|
+
}
|
|
978
|
+
|
|
917
979
|
// Snapshot the previous session's message ids. On a same-tab session swap
|
|
918
980
|
// (edit→fork / version switch) a queued send may append optimistic + live
|
|
919
981
|
// streaming messages BEFORE this history fetch resolves. We drop exactly the
|
|
@@ -970,7 +1032,7 @@ export function useChat(sessionId: string | null, providerId = "claude", project
|
|
|
970
1032
|
return () => {
|
|
971
1033
|
cancelled = true;
|
|
972
1034
|
};
|
|
973
|
-
}, [sessionId, providerId, projectName]);
|
|
1035
|
+
}, [sessionId, providerId, projectName, updateTeamActivity, loadTeamDetail]);
|
|
974
1036
|
|
|
975
1037
|
const sendMessage = useCallback(
|
|
976
1038
|
(content: string, opts?: { permissionMode?: string; priority?: 'now' | 'next' | 'later'; images?: Array<{ data: string; mediaType: string }> }) => {
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Poll a team's per-member activity while the panel is open.
|
|
3
|
+
*
|
|
4
|
+
* The team inboxes cannot say who is working — every member reads "active"
|
|
5
|
+
* forever there. `/api/teams/:name/activity` derives it from the agent
|
|
6
|
+
* transcripts instead, and also returns the replies teammates sent back, which
|
|
7
|
+
* no inbox holds. Polling only runs while `enabled`, so a closed panel costs
|
|
8
|
+
* nothing.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
12
|
+
import { api } from "@/lib/api-client";
|
|
13
|
+
|
|
14
|
+
/** Fast enough that a working teammate looks live, slow enough to stay cheap. */
|
|
15
|
+
const POLL_INTERVAL_MS = 5_000;
|
|
16
|
+
|
|
17
|
+
export type MemberWorkState = "working" | "paused" | "no-transcript";
|
|
18
|
+
|
|
19
|
+
export interface TeamMemberActivity {
|
|
20
|
+
name: string;
|
|
21
|
+
workState: MemberWorkState;
|
|
22
|
+
agentType?: string;
|
|
23
|
+
model?: string;
|
|
24
|
+
description?: string;
|
|
25
|
+
toolUseId?: string;
|
|
26
|
+
startedAt?: string;
|
|
27
|
+
lastEventAt?: string;
|
|
28
|
+
lastTool?: string;
|
|
29
|
+
lastToolArg?: string;
|
|
30
|
+
lastNarrative?: string;
|
|
31
|
+
sizeBytes: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface OutboundTeamMessage {
|
|
35
|
+
from: string;
|
|
36
|
+
to: string;
|
|
37
|
+
text: string;
|
|
38
|
+
summary?: string;
|
|
39
|
+
timestamp: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface TeamActivityFeed {
|
|
43
|
+
members: TeamMemberActivity[];
|
|
44
|
+
outbound: OutboundTeamMessage[];
|
|
45
|
+
loading: boolean;
|
|
46
|
+
/** Re-fetch now, for the panel's refresh button. */
|
|
47
|
+
refresh: () => void;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function useTeamActivityFeed(teamName: string, enabled: boolean): TeamActivityFeed {
|
|
51
|
+
const [members, setMembers] = useState<TeamMemberActivity[]>([]);
|
|
52
|
+
const [outbound, setOutbound] = useState<OutboundTeamMessage[]>([]);
|
|
53
|
+
const [loading, setLoading] = useState(false);
|
|
54
|
+
// Survives re-renders so a poll that resolves after teardown is discarded.
|
|
55
|
+
const cancelledRef = useRef(false);
|
|
56
|
+
|
|
57
|
+
const fetchActivity = useCallback(async (team: string) => {
|
|
58
|
+
if (!team) return;
|
|
59
|
+
setLoading(true);
|
|
60
|
+
try {
|
|
61
|
+
const res = await api.get<{ members?: TeamMemberActivity[]; outbound?: OutboundTeamMessage[] }>(
|
|
62
|
+
`/api/teams/${encodeURIComponent(team)}/activity`,
|
|
63
|
+
);
|
|
64
|
+
if (cancelledRef.current) return;
|
|
65
|
+
setMembers(res?.members ?? []);
|
|
66
|
+
setOutbound(res?.outbound ?? []);
|
|
67
|
+
} catch {
|
|
68
|
+
/* keep the last good snapshot rather than blanking the list */
|
|
69
|
+
} finally {
|
|
70
|
+
if (!cancelledRef.current) setLoading(false);
|
|
71
|
+
}
|
|
72
|
+
}, []);
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
cancelledRef.current = false;
|
|
76
|
+
if (!enabled || !teamName) {
|
|
77
|
+
// Drop the previous team's rows so a reopen never shows stale members.
|
|
78
|
+
setMembers([]);
|
|
79
|
+
setOutbound([]);
|
|
80
|
+
return () => { cancelledRef.current = true; };
|
|
81
|
+
}
|
|
82
|
+
void fetchActivity(teamName);
|
|
83
|
+
const timer = setInterval(() => void fetchActivity(teamName), POLL_INTERVAL_MS);
|
|
84
|
+
return () => {
|
|
85
|
+
cancelledRef.current = true;
|
|
86
|
+
clearInterval(timer);
|
|
87
|
+
};
|
|
88
|
+
}, [teamName, enabled, fetchActivity]);
|
|
89
|
+
|
|
90
|
+
const refresh = useCallback(() => void fetchActivity(teamName), [fetchActivity, teamName]);
|
|
91
|
+
return { members, outbound, loading, refresh };
|
|
92
|
+
}
|