@jetcrab/pi-desk 1.1.18-dev → 1.1.21
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/docs/plugins.md +82 -2
- package/next.config.ts +1 -0
- package/package.json +4 -3
- package/src/app/page.tsx +6 -1
- package/src/app/project-preview/page.tsx +10 -0
- package/src/client/l1_entry/pages/l1-home-page.tsx +30 -0
- package/src/client/l1_entry/pages/l1-project-preview-page.tsx +33 -0
- package/src/client/l1_entry/providers/l1-app-providers.tsx +18 -15
- package/src/client/l1_entry/providers/l1-app-runtime-provider.tsx +2 -2
- package/src/client/l2_biz/auth/l2-login-biz.ts +3 -0
- package/src/client/l2_biz/auth/l2-login.tsx +2 -3
- package/src/client/l2_biz/plugin-host/l2-plugin-capability-details.tsx +6 -1
- package/src/client/l2_biz/plugin-host/l2-plugin-contribution-host.tsx +71 -10
- package/src/client/l2_biz/plugin-host/l2-plugin-management-controls.tsx +9 -0
- package/src/client/l2_biz/plugin-host/l2-plugin-session-header-layout.ts +18 -0
- package/src/client/l2_biz/plugin-host/l2-plugin-session-header.tsx +247 -0
- package/src/client/l2_biz/plugin-host/l2-plugin-widgets.tsx +50 -0
- package/src/client/l2_biz/plugin-host/views/l2-plugin-session-header-action.tsx +112 -0
- package/src/client/l2_biz/project-preview/hooks/l2-use-project-picker.ts +76 -0
- package/src/client/l2_biz/project-preview/hooks/l2-use-project-preview.ts +214 -0
- package/src/client/l2_biz/project-preview/l2-project-preview-biz.ts +49 -4
- package/src/client/l2_biz/project-preview/l2-project-preview-dialog.tsx +42 -450
- package/src/client/l2_biz/project-preview/l2-project-preview-page.tsx +61 -0
- package/src/client/l2_biz/project-preview/l2-project-preview.tsx +342 -0
- package/src/client/l2_biz/project-preview/runtime/l2-project-history-runtime.ts +9 -3
- package/src/client/l2_biz/project-preview/views/l2-project-picker.tsx +168 -0
- package/src/client/l2_biz/settings/hooks/l2-use-model-settings.ts +34 -31
- package/src/client/l2_biz/settings/views/l2-model-replacement-preview.tsx +7 -1
- package/src/client/l2_biz/workbench/file-workspace/l2-file-workspace-runtime.ts +1 -1
- package/src/client/l2_biz/workbench/file-workspace/l2-project-preview-biz.ts +4 -3
- package/src/client/l2_biz/workbench/hooks/l2-workbench-chat-reload.ts +42 -0
- package/src/client/l2_biz/workbench/hooks/l2-workbench-chat-viewport.ts +28 -21
- package/src/client/l2_biz/workbench/l2-work-session-columns.module.css +32 -0
- package/src/client/l2_biz/workbench/l2-work-session-columns.tsx +55 -16
- package/src/client/l2_biz/workbench/l2-work-session-sidebar.tsx +38 -9
- package/src/client/l2_biz/workbench/l2-workbench-chat-composer.tsx +12 -1
- package/src/client/l2_biz/workbench/l2-workbench-chat.ts +45 -0
- package/src/client/l2_biz/workbench/l2-workbench-initialization-notice.tsx +8 -3
- package/src/client/l2_biz/workbench/l2-workbench-plugin-slots.ts +13 -0
- package/src/client/l2_biz/workbench/l2-workbench.tsx +19 -2
- package/src/client/l3_modules/conversation/l3-conversation-markdown.tsx +10 -1
- package/src/client/l3_modules/conversation/l3-conversation-viewport.tsx +12 -6
- package/src/client/l3_modules/project-files/l3-markdown-preview.module.css +1 -1
- package/src/client/l3_modules/project-files/l3-markdown-preview.tsx +10 -1
- package/src/client/l3_modules/project-files/l3-project-document-git.ts +4 -3
- package/src/client/l3_modules/project-files/l3-project-document-view.tsx +6 -1
- package/src/client/l3_modules/project-files/l3-project-file-browser.tsx +171 -229
- package/src/client/l3_modules/project-files/l3-project-file-runtime.ts +133 -70
- package/src/client/l3_modules/project-files/l3-project-reader.ts +26 -37
- package/src/client/l3_modules/project-files/views/l3-project-search-controls.tsx +201 -0
- package/src/client/l3_modules/project-files/views/l3-project-search-results.tsx +333 -0
- package/src/client/l4_foundation/lib/l4-client-id.tsx +11 -0
- package/src/client/l4_foundation/plugin-host/l4-plugin-host-runtime.ts +316 -35
- package/src/client/l4_foundation/ui/code/l4-code-preview.tsx +47 -3
- package/src/client/l4_foundation/ui/markdown/l4-markdown-remark.ts +89 -0
- package/src/client/l4_foundation/ui/markdown/l4-markdown.module.css +58 -0
- package/src/client/l4_foundation/ui/markdown/l4-markdown.tsx +108 -0
- package/src/common/l2_biz/auth/l2-auth-return-to.ts +41 -0
- package/src/common/l2_biz/model-settings/l2-model-settings-contract.ts +5 -1
- package/src/common/l2_biz/plugin/l2-plugin-management-messages.ts +4 -0
- package/src/common/l2_biz/project-preview/l2-project-history-messages.ts +28 -0
- package/src/common/l2_biz/project-preview/l2-project-preview-navigation.ts +5 -0
- package/src/common/l2_biz/settings/l2-settings-messages.ts +4 -3
- package/src/common/l2_biz/work-session/l2-work-session-file-contract.ts +4 -3
- package/src/common/l2_biz/workbench/l2-workbench-locale-messages.ts +8 -0
- package/src/common/l3_modules/project-files/l3-project-files-contract.ts +2 -3
- package/src/common/l3_modules/project-files/l3-project-files-locale-messages.ts +56 -10
- package/src/common/l4_foundation/file/l4-project-file-contract.ts +51 -1
- package/src/common/l4_foundation/locale/l4-host-error-messages.ts +11 -0
- package/src/common/l4_foundation/locale/l4-locale-messages.ts +16 -0
- package/src/instrumentation.ts +0 -2
- package/src/server/l1_entry/api/l1-project-read-routes.ts +6 -4
- package/src/server/l1_entry/api/l1-project-route.ts +9 -8
- package/src/server/l1_entry/api/l1-work-session-file-error.ts +23 -0
- package/src/server/l1_entry/node/l1-server.ts +8 -4
- package/src/server/l2_biz/plugin-management/l2-plugin-management.ts +0 -3
- package/src/server/l2_biz/work-session/l2-work-session-chat-runtime.ts +30 -58
- package/src/server/l2_biz/work-session/l2-work-session-manage.ts +0 -4
- package/src/server/l4_foundation/file/l4-project-file-index.ts +276 -0
- package/src/server/l4_foundation/file/l4-project-search.ts +399 -0
- package/src/server/l4_foundation/file/l4-ripgrep-search.ts +192 -0
- package/src/server/l4_foundation/file/l4-work-session-project-file.ts +8 -332
- package/src/server/l4_foundation/model-settings/l4-model-settings-store.ts +29 -20
- package/src/server/l4_foundation/model-settings/l4-model-settings-types.ts +1 -1
- package/src/server/l4_foundation/pi/l4-pi-chat-worker.ts +22 -5
- package/src/server/l4_foundation/pi/l4-pi-package-maintenance.ts +0 -6
- package/src/server/l4_foundation/pi/l4-pi-plugin-capability-probe.ts +1 -3
- package/src/server/l4_foundation/pi/l4-pi-plugin-owner-runtime.ts +133 -13
- package/src/server/l4_foundation/pi/l4-pi-plugin-probe.ts +1 -3
- package/src/server/l4_foundation/pi/l4-pi-runner-process.ts +2 -2
- package/src/server/l4_foundation/pi/l4-pi-session-initialization.ts +9 -0
- package/src/server/l4_foundation/process/l4-process-tree.js +23 -4
- package/temp/build/pi-desk/release/.next/BUILD_ID +1 -1
- package/temp/build/pi-desk/release/.next/app-path-routes-manifest.json +3 -2
- package/temp/build/pi-desk/release/.next/build-manifest.json +3 -3
- package/temp/build/pi-desk/release/.next/prerender-manifest.json +3 -3
- package/temp/build/pi-desk/release/.next/react-loadable-manifest.json +1 -2
- package/temp/build/pi-desk/release/.next/required-server-files.js +4 -3
- package/temp/build/pi-desk/release/.next/required-server-files.json +4 -3
- package/temp/build/pi-desk/release/.next/routes-manifest.json +6 -0
- package/temp/build/pi-desk/release/.next/server/app/_global-error/page.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.html +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_global-error.rsc +3 -3
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_full.segment.rsc +3 -3
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +3 -3
- package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_not-found/page.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/auth/login/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/auth/logout/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/auth-settings/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/auth-settings/replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/capability-modes/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/capability-modes/replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/chat/context/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/chat/images/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/chat/messages/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/health/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/mcp-check/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/del/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/model-catalog/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/model-settings/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/model-settings/preview/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/model-settings/replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/pi/directories/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-entries/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-ignores/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-ignores/replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/pi/models/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/pi/session-user-messages/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/pi/sessions/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/search/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/settings-get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/settings-replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/add/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/apply/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/batch/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/del/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/enabled-set/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/reload/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/plugins/update/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-files/get/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-files/list/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-files/reveal/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-files/search/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git/get/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-branches/list/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-changes/list/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-diff/get/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-heads/get/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-log/list/route.js +2 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-model-default/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-model-default/replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/skill-files/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/skill-files/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/skill-files/replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/skills/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/task-center/images/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/task-center/messages/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-session-git-branch/add/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-session-git-branch/replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-session-tree/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-session-tree-entry/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/add/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/branch/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/del/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/rename/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/replace/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/sort/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/favicon.ico/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/login/page.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/page.js +72 -72
- package/temp/build/pi-desk/release/.next/server/app/page_client-reference-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/project-preview/page.js +1 -0
- package/temp/build/pi-desk/release/.next/server/app/project-preview/page_client-reference-manifest.js +1 -0
- package/temp/build/pi-desk/release/.next/server/app-paths-manifest.json +3 -2
- package/temp/build/pi-desk/release/.next/server/chunks/10912.js +2 -2
- package/temp/build/pi-desk/release/.next/server/chunks/20460.js +15 -0
- package/temp/build/pi-desk/release/.next/server/chunks/23054.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/25406.js +10 -10
- package/temp/build/pi-desk/release/.next/server/chunks/35985.js +3 -17
- package/temp/build/pi-desk/release/.next/server/chunks/36361.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/48180.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/50520.js +2 -4
- package/temp/build/pi-desk/release/.next/server/chunks/56830.js +235 -0
- package/temp/build/pi-desk/release/.next/server/chunks/6917.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/70780.js +1 -0
- package/temp/build/pi-desk/release/.next/server/chunks/74528.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/78262.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/79686.js +62 -0
- package/temp/build/pi-desk/release/.next/server/chunks/80780.js +1 -0
- package/temp/build/pi-desk/release/.next/server/chunks/81363.js +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/82499.js +4 -4
- package/temp/build/pi-desk/release/.next/server/chunks/92386.js +8 -8
- package/temp/build/pi-desk/release/.next/server/instrumentation.js +1 -1
- package/temp/build/pi-desk/release/.next/server/middleware-build-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/temp/build/pi-desk/release/.next/server/pages/500.html +1 -1
- package/temp/build/pi-desk/release/.next/server/server-reference-manifest.json +1 -1
- package/temp/build/pi-desk/release/.next/static/8Yd33M0zPi4tbRn4xCaRY/_buildManifest.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/11937-1d0a9822f2c7995b.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/14825-5579aed34a631ad7.js +62 -0
- package/temp/build/pi-desk/release/.next/static/chunks/15302-9c807afc6c60de0c.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/19991.8aa2e4b6df15fad6.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/22092-6d1bfbefa825ec03.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/37313-d5970e2d0c270d22.js +238 -0
- package/temp/build/pi-desk/release/.next/static/chunks/{49516-cc4dcd84d64ab4fe.js → 51474-8dc714cb76b2e60a.js} +2 -2
- package/temp/build/pi-desk/release/.next/static/chunks/57235-03ba1044f22b6c84.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/79267-7d21d7cdbd7cfca1.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/82371-f7ba4a07a5dbcf34.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/9038-03144ed96d613f75.js +15 -0
- package/temp/build/pi-desk/release/.next/static/chunks/app/{error-0296a3b20cd1bf00.js → error-216b14b5da089ed3.js} +1 -1
- package/temp/build/pi-desk/release/.next/static/chunks/app/layout-20718f1503910291.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/app/login/page-0d3d4827802108e5.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/app/{not-found-2aab8b33ebbf3257.js → not-found-720cdf6a606b821a.js} +1 -1
- package/temp/build/pi-desk/release/.next/static/chunks/app/page-6ebc1c877f6025dd.js +158 -0
- package/temp/build/pi-desk/release/.next/static/chunks/app/project-preview/page-d70ee4e9409b970a.js +1 -0
- package/temp/build/pi-desk/release/.next/static/chunks/webpack-b60891a214e0925d.js +1 -0
- package/temp/build/pi-desk/release/.next/static/css/1e780a4936aa6ba3.css +1 -0
- package/temp/build/pi-desk/release/.next/static/css/6dbae2922f88ca31.css +1 -0
- package/temp/build/pi-desk/release/.next/static/css/f646ca6c07245c4c.css +1 -0
- package/temp/build/pi-desk/release/.next/trace-build +1 -1
- package/temp/build/pi-desk/release/.next/server/chunks/33658.js +0 -296
- package/temp/build/pi-desk/release/.next/server/chunks/8584.js +0 -2
- package/temp/build/pi-desk/release/.next/static/chunks/31087-ba426d09d6f68bfd.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/3814-35cfab0ce3aa7c41.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/39654-b5fedd762ea0312e.js +0 -238
- package/temp/build/pi-desk/release/.next/static/chunks/70669.07f3670bfb7d6dd2.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/74220-795a2e7185bd66cb.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/75130-d2ca74894aff8085.js +0 -13
- package/temp/build/pi-desk/release/.next/static/chunks/78923-17a9c337b29dcecd.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/81881-f11dbe7dc0d04ed9.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/96995-8f8313ea2ac3abd7.js +0 -62
- package/temp/build/pi-desk/release/.next/static/chunks/app/layout-c0e1c5cef8620cf8.js +0 -3
- package/temp/build/pi-desk/release/.next/static/chunks/app/login/page-84892b06243891f0.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/app/page-a984139e99586041.js +0 -158
- package/temp/build/pi-desk/release/.next/static/chunks/webpack-40a782fc8a3535b9.js +0 -1
- package/temp/build/pi-desk/release/.next/static/css/6427d02b6edf406f.css +0 -1
- package/temp/build/pi-desk/release/.next/static/css/79e06f05df9edff0.css +0 -1
- package/temp/build/pi-desk/release/.next/static/n2LrXQgq8UkbH_AzhR3fw/_buildManifest.js +0 -1
- /package/temp/build/pi-desk/release/.next/static/{n2LrXQgq8UkbH_AzhR3fw → 8Yd33M0zPi4tbRn4xCaRY}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useMemo, useSyncExternalStore } from 'react'
|
|
4
|
+
import { useTranslation } from 'react-i18next'
|
|
5
|
+
import type { L2WorkSessionListItem } from '@common/l2_biz/work-session/l2-work-session-contract'
|
|
6
|
+
import { useL4AppToast } from '@client/l4_foundation/ui/l4-app-toast'
|
|
7
|
+
import type { L2WorkbenchChatRuntime } from '../l2-workbench-chat'
|
|
8
|
+
|
|
9
|
+
export function useL2WorkbenchChatReload(
|
|
10
|
+
runtime: L2WorkbenchChatRuntime,
|
|
11
|
+
workSessions: readonly L2WorkSessionListItem[]
|
|
12
|
+
): {
|
|
13
|
+
syncingWorkIds: readonly string[]
|
|
14
|
+
reload: (workSession: L2WorkSessionListItem) => void
|
|
15
|
+
} {
|
|
16
|
+
const { t } = useTranslation('workbench')
|
|
17
|
+
const toast = useL4AppToast()
|
|
18
|
+
const getSyncing = useCallback(
|
|
19
|
+
(): string =>
|
|
20
|
+
JSON.stringify(
|
|
21
|
+
workSessions
|
|
22
|
+
.filter(
|
|
23
|
+
({ workId, sessionId, branchId }) =>
|
|
24
|
+
runtime.getSourceState({ workId, sessionId, branchId }).syncStatus === 'syncing'
|
|
25
|
+
)
|
|
26
|
+
.map((session) => session.workId)
|
|
27
|
+
),
|
|
28
|
+
[runtime, workSessions]
|
|
29
|
+
)
|
|
30
|
+
const syncing = useSyncExternalStore(runtime.subscribe, getSyncing, getSyncing)
|
|
31
|
+
const syncingWorkIds = useMemo((): string[] => JSON.parse(syncing), [syncing])
|
|
32
|
+
const reload = useCallback(
|
|
33
|
+
(session: L2WorkSessionListItem): void => {
|
|
34
|
+
const { workId, sessionId, branchId } = session
|
|
35
|
+
void runtime.reloadMessages({ workId, sessionId, branchId }).catch((cause: unknown) => {
|
|
36
|
+
toast.error(cause instanceof Error ? cause.message : t('reloadChatDataFailed'))
|
|
37
|
+
})
|
|
38
|
+
},
|
|
39
|
+
[runtime, t, toast]
|
|
40
|
+
)
|
|
41
|
+
return { syncingWorkIds, reload }
|
|
42
|
+
}
|
|
@@ -7,7 +7,8 @@ import {
|
|
|
7
7
|
clearL3ConversationTailSpacer,
|
|
8
8
|
findL3ConversationMessage,
|
|
9
9
|
logL3ConversationScroll,
|
|
10
|
-
positionL3ConversationMessage
|
|
10
|
+
positionL3ConversationMessage,
|
|
11
|
+
refreshL3ConversationTailSpacer
|
|
11
12
|
} from '@client/l3_modules/conversation/l3-conversation-viewport'
|
|
12
13
|
import type { L2WorkbenchChatViewportSnapshot } from '../l2-workbench-chat'
|
|
13
14
|
|
|
@@ -252,25 +253,18 @@ export function L2WorkbenchChatViewportManager({
|
|
|
252
253
|
const enterRunningAtBottom =
|
|
253
254
|
entryPosition?.follow ?? (initializing && running && snapshot === null)
|
|
254
255
|
const entryTarget = target
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
// 首屏懒布局展开后也在绘制前置底,不交给后续缓动收敛。
|
|
264
|
-
scrollElement.scrollTop = Math.max(
|
|
265
|
-
0,
|
|
266
|
-
scrollElement.scrollHeight - scrollElement.clientHeight
|
|
267
|
-
)
|
|
268
|
-
}
|
|
269
|
-
markProgrammaticScroll(state, scrollElement)
|
|
270
|
-
// 同步确立跟随,立即切走也不能丢失这个状态。
|
|
271
|
-
if (followAfterPosition || !entryTarget) void scrollToBottom({ animation: 'instant' })
|
|
256
|
+
stopScroll()
|
|
257
|
+
if (entryTarget) {
|
|
258
|
+
positionL3ConversationMessage(scrollElement, entryTarget, targetOffset, containsTail)
|
|
259
|
+
} else {
|
|
260
|
+
clearL3ConversationTailSpacer(scrollElement)
|
|
261
|
+
}
|
|
262
|
+
if (enterRunningAtBottom || !entryTarget) {
|
|
263
|
+
scrollElement.scrollTop = Math.max(0, state.calculatedTargetScrollTop)
|
|
272
264
|
}
|
|
273
|
-
|
|
265
|
+
markProgrammaticScroll(state, scrollElement)
|
|
266
|
+
// 同步确立跟随,立即切走也不能丢失这个状态。
|
|
267
|
+
if (followAfterPosition || !entryTarget) void scrollToBottom({ animation: 'instant' })
|
|
274
268
|
|
|
275
269
|
logL3ConversationScroll('viewport:initialize:positioned', scrollElement, {
|
|
276
270
|
initializing,
|
|
@@ -288,7 +282,18 @@ export function L2WorkbenchChatViewportManager({
|
|
|
288
282
|
anchor: anchor ? { messageId: anchor, offset: targetOffset } : null,
|
|
289
283
|
follow: followAfterPosition || target === null
|
|
290
284
|
}
|
|
291
|
-
const observer = new ResizeObserver(
|
|
285
|
+
const observer = new ResizeObserver(() => {
|
|
286
|
+
// 懒布局与拖宽只校正既定目标,不再重走提问定位、停止和启动跟随。
|
|
287
|
+
if (followAfterPosition || !entryTarget) {
|
|
288
|
+
refreshL3ConversationTailSpacer(scrollElement)
|
|
289
|
+
scrollElement.scrollTop = Math.max(0, state.calculatedTargetScrollTop)
|
|
290
|
+
} else {
|
|
291
|
+
// 内容收缩时库可能自动恢复跟随;首屏阅读锚点仍优先。
|
|
292
|
+
if (state.isAtBottom) stopScroll()
|
|
293
|
+
positionL3ConversationMessage(scrollElement, entryTarget, targetOffset, containsTail)
|
|
294
|
+
}
|
|
295
|
+
markProgrammaticScroll(state, scrollElement)
|
|
296
|
+
})
|
|
292
297
|
observer.observe(scrollElement)
|
|
293
298
|
if (scrollElement.firstElementChild) observer.observe(scrollElement.firstElementChild)
|
|
294
299
|
const interactionElement = scrollElement.parentElement!
|
|
@@ -356,13 +361,15 @@ export function L2WorkbenchChatViewportManager({
|
|
|
356
361
|
isAtBottom: state.isAtBottom,
|
|
357
362
|
escapedFromLock: state.escapedFromLock
|
|
358
363
|
})
|
|
364
|
+
// 新视图会在旧视图清理前读取快照,恢复跟随时就释放旧阅读位置。
|
|
365
|
+
onClearSnapshot()
|
|
359
366
|
void scrollToBottom({ animation: 'instant' })
|
|
360
367
|
}
|
|
361
368
|
}
|
|
362
369
|
|
|
363
370
|
scrollElement.addEventListener('scroll', resumeFollowAtBottom, { passive: true })
|
|
364
371
|
return () => scrollElement.removeEventListener('scroll', resumeFollowAtBottom)
|
|
365
|
-
}, [latestUserAnchor, scrollRef, scrollToBottom, state, visible])
|
|
372
|
+
}, [latestUserAnchor, onClearSnapshot, scrollRef, scrollToBottom, state, visible])
|
|
366
373
|
|
|
367
374
|
useLayoutEffect(() => {
|
|
368
375
|
if (!running || !visible) return
|
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
.chatContainer {
|
|
2
|
+
container-name: work-session-chat;
|
|
3
|
+
container-type: inline-size;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.headerActionIcon {
|
|
7
|
+
display: none;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@container work-session-chat (width < 28rem) {
|
|
11
|
+
.sessionHeader {
|
|
12
|
+
gap: 0.5rem;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.headerAction {
|
|
16
|
+
width: 1.75rem;
|
|
17
|
+
height: 1.75rem;
|
|
18
|
+
min-height: 1.75rem;
|
|
19
|
+
gap: 0;
|
|
20
|
+
border-radius: 0.375rem;
|
|
21
|
+
padding: 0;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.headerActionLabel {
|
|
25
|
+
display: none;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.headerActionIcon {
|
|
29
|
+
display: block;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
1
33
|
.collapsedText {
|
|
2
34
|
overflow: hidden;
|
|
3
35
|
text-overflow: ellipsis;
|
|
@@ -54,8 +54,10 @@ import { useL2WorkSessionColumnCapacity } from './hooks/l2-work-session-column-c
|
|
|
54
54
|
import type { L2FileWorkspaceRuntime } from './file-workspace/l2-file-workspace-runtime'
|
|
55
55
|
import { L2WorkSessionFileLayout } from './file-workspace/l2-work-session-file-layout'
|
|
56
56
|
import { Button, buttonVariants } from '@client/l4_foundation/ui/shadcn/button'
|
|
57
|
+
import type { L2WorkbenchPluginSessionSlots } from './l2-workbench-plugin-slots'
|
|
57
58
|
|
|
58
|
-
interface L2WorkSessionColumnsProps
|
|
59
|
+
interface L2WorkSessionColumnsProps
|
|
60
|
+
extends L2WorkbenchComposerSlots, L2WorkbenchPluginSessionSlots {
|
|
59
61
|
onOpenSettings?: (target?: 'model-presets') => void
|
|
60
62
|
workSessions: readonly L2WorkSessionListItem[]
|
|
61
63
|
primaryWorkId: string | null
|
|
@@ -126,7 +128,7 @@ function SidebarToggle({
|
|
|
126
128
|
)
|
|
127
129
|
}
|
|
128
130
|
|
|
129
|
-
interface WorkSessionColumnProps extends L2WorkbenchComposerSlots {
|
|
131
|
+
interface WorkSessionColumnProps extends L2WorkbenchComposerSlots, L2WorkbenchPluginSessionSlots {
|
|
130
132
|
onOpenSettings?: (target?: 'model-presets') => void
|
|
131
133
|
workSession: L2WorkSessionListItem
|
|
132
134
|
fixed: boolean
|
|
@@ -181,6 +183,8 @@ const WorkSessionColumn = memo(function WorkSessionColumn({
|
|
|
181
183
|
renderTaskSummary,
|
|
182
184
|
renderTaskMenu,
|
|
183
185
|
renderPluginComposerPanel,
|
|
186
|
+
renderPluginWidgets,
|
|
187
|
+
renderPluginSessionHeader,
|
|
184
188
|
gitRuntime,
|
|
185
189
|
invokePluginMethod,
|
|
186
190
|
listPiCommands,
|
|
@@ -214,6 +218,34 @@ const WorkSessionColumn = memo(function WorkSessionColumn({
|
|
|
214
218
|
useSyncExternalStore(fileRuntime.subscribe, fileRuntime.getRevision, fileRuntime.getRevision)
|
|
215
219
|
const fileWorkspace = fileRuntime.getWorkspace(workSession.workId)
|
|
216
220
|
const fileCount = (fileWorkspace?.tabs.length ?? 0) + (fileWorkspace?.activeDiff ? 1 : 0)
|
|
221
|
+
const source = useMemo<L2ChatSource>(
|
|
222
|
+
() => ({
|
|
223
|
+
workId: workSession.workId,
|
|
224
|
+
sessionId: workSession.sessionId,
|
|
225
|
+
branchId: workSession.branchId
|
|
226
|
+
}),
|
|
227
|
+
[workSession.workId, workSession.sessionId, workSession.branchId]
|
|
228
|
+
)
|
|
229
|
+
const title = (
|
|
230
|
+
<div className="min-w-0">
|
|
231
|
+
<p
|
|
232
|
+
className={cn(
|
|
233
|
+
'truncate text-sm transition-colors duration-150 motion-reduce:transition-none',
|
|
234
|
+
focused ? 'font-semibold text-foreground' : 'font-medium text-muted-foreground'
|
|
235
|
+
)}
|
|
236
|
+
title={workSession.projectName}
|
|
237
|
+
>
|
|
238
|
+
{workSession.projectName}
|
|
239
|
+
</p>
|
|
240
|
+
<p
|
|
241
|
+
className="truncate text-xs text-muted-foreground"
|
|
242
|
+
data-testid="session-header-title"
|
|
243
|
+
title={workSession.sessionTitle ?? t('newSession')}
|
|
244
|
+
>
|
|
245
|
+
{workSession.sessionTitle ?? t('newSession')}
|
|
246
|
+
</p>
|
|
247
|
+
</div>
|
|
248
|
+
)
|
|
217
249
|
|
|
218
250
|
useEffect(() => {
|
|
219
251
|
const column = columnRef.current
|
|
@@ -259,23 +291,18 @@ const WorkSessionColumn = memo(function WorkSessionColumn({
|
|
|
259
291
|
onOpenSidebar={() => onOpenFileSidebar(workSession.workId)}
|
|
260
292
|
>
|
|
261
293
|
{(chatVisible) => (
|
|
262
|
-
<div className=
|
|
263
|
-
<header
|
|
294
|
+
<div className={cn(styles.chatContainer, 'flex h-full min-h-0 min-w-0 flex-1 flex-col')}>
|
|
295
|
+
<header
|
|
296
|
+
className={cn(styles.sessionHeader, 'flex min-h-14 shrink-0 items-center gap-3 px-3')}
|
|
297
|
+
data-testid="session-header"
|
|
298
|
+
>
|
|
264
299
|
{fixed && !mobile ? null : (
|
|
265
300
|
<SidebarToggle sidebarOpen={sidebarOpen} onToggle={onToggleSidebar} />
|
|
266
301
|
)}
|
|
267
302
|
<div className="min-w-0 flex-1">
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
focused ? 'font-semibold text-foreground' : 'font-medium text-muted-foreground'
|
|
272
|
-
)}
|
|
273
|
-
>
|
|
274
|
-
{workSession.projectName}
|
|
275
|
-
</p>
|
|
276
|
-
<p className="truncate text-xs text-muted-foreground">
|
|
277
|
-
{workSession.sessionTitle ?? t('newSession')}
|
|
278
|
-
</p>
|
|
303
|
+
{chatVisible && contentActive
|
|
304
|
+
? renderPluginSessionHeader({ source, title })
|
|
305
|
+
: title}
|
|
279
306
|
</div>
|
|
280
307
|
<div className="-mr-1 flex shrink-0 items-center">
|
|
281
308
|
{initializationIssue.error ? (
|
|
@@ -292,13 +319,17 @@ const WorkSessionColumn = memo(function WorkSessionColumn({
|
|
|
292
319
|
{fileCount > 0 ? (
|
|
293
320
|
<Button
|
|
294
321
|
variant="ghost"
|
|
322
|
+
className={styles.headerAction}
|
|
295
323
|
data-testid="session-files-entry"
|
|
296
324
|
aria-label={t('openSessionFiles')}
|
|
325
|
+
title={t('sessionFiles', { count: fileCount })}
|
|
297
326
|
aria-pressed={fileWorkspace?.windowOpen ?? false}
|
|
298
327
|
onClick={() => fileRuntime.showWindow(workSession.workId)}
|
|
299
328
|
>
|
|
300
329
|
<FilesIcon className="size-4" />
|
|
301
|
-
|
|
330
|
+
<span className={styles.headerActionLabel}>
|
|
331
|
+
{t('sessionFiles', { count: fileCount })}
|
|
332
|
+
</span>
|
|
302
333
|
</Button>
|
|
303
334
|
) : null}
|
|
304
335
|
{onUnpinWorkSession ? (
|
|
@@ -338,6 +369,8 @@ const WorkSessionColumn = memo(function WorkSessionColumn({
|
|
|
338
369
|
renderTaskSummary={renderTaskSummary}
|
|
339
370
|
renderTaskMenu={renderTaskMenu}
|
|
340
371
|
renderPluginComposerPanel={renderPluginComposerPanel}
|
|
372
|
+
renderPluginWidgets={renderPluginWidgets}
|
|
373
|
+
widgetsVisible={chatVisible && contentActive}
|
|
341
374
|
gitRuntime={gitRuntime}
|
|
342
375
|
active={composerActive}
|
|
343
376
|
mobile={mobile}
|
|
@@ -473,6 +506,8 @@ export function L2WorkSessionColumns({
|
|
|
473
506
|
renderTaskSummary,
|
|
474
507
|
renderTaskMenu,
|
|
475
508
|
renderPluginComposerPanel,
|
|
509
|
+
renderPluginWidgets,
|
|
510
|
+
renderPluginSessionHeader,
|
|
476
511
|
gitRuntime,
|
|
477
512
|
invokePluginMethod,
|
|
478
513
|
listPiCommands,
|
|
@@ -660,6 +695,8 @@ export function L2WorkSessionColumns({
|
|
|
660
695
|
renderTaskSummary={renderTaskSummary}
|
|
661
696
|
renderTaskMenu={renderTaskMenu}
|
|
662
697
|
renderPluginComposerPanel={renderPluginComposerPanel}
|
|
698
|
+
renderPluginWidgets={renderPluginWidgets}
|
|
699
|
+
renderPluginSessionHeader={renderPluginSessionHeader}
|
|
663
700
|
gitRuntime={gitRuntime}
|
|
664
701
|
invokePluginMethod={invokePluginMethod}
|
|
665
702
|
listPiCommands={listPiCommands}
|
|
@@ -758,6 +795,8 @@ export function L2WorkSessionColumns({
|
|
|
758
795
|
renderTaskSummary={renderTaskSummary}
|
|
759
796
|
renderTaskMenu={renderTaskMenu}
|
|
760
797
|
renderPluginComposerPanel={renderPluginComposerPanel}
|
|
798
|
+
renderPluginWidgets={renderPluginWidgets}
|
|
799
|
+
renderPluginSessionHeader={renderPluginSessionHeader}
|
|
761
800
|
gitRuntime={gitRuntime}
|
|
762
801
|
invokePluginMethod={invokePluginMethod}
|
|
763
802
|
listPiCommands={listPiCommands}
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
EllipsisIcon,
|
|
22
22
|
FolderTreeIcon,
|
|
23
23
|
FolderSearchIcon,
|
|
24
|
+
ExternalLinkIcon,
|
|
24
25
|
LoaderCircleIcon,
|
|
25
26
|
MessageSquareTextIcon,
|
|
26
27
|
PinIcon,
|
|
@@ -131,6 +132,7 @@ interface L2WorkSessionSidebarProps {
|
|
|
131
132
|
onModeChange: (mode: L2WorkSessionSidebarMode) => void
|
|
132
133
|
onRefreshFiles: () => void
|
|
133
134
|
onPreviewProject: () => void
|
|
135
|
+
onPreviewProjectWindow: () => void
|
|
134
136
|
onOpenTerminal: () => void
|
|
135
137
|
onCreate: () => void
|
|
136
138
|
onReplace: () => void
|
|
@@ -140,6 +142,8 @@ interface L2WorkSessionSidebarProps {
|
|
|
140
142
|
onTogglePin: (workId: string) => void
|
|
141
143
|
onDelete: (workId: string) => void
|
|
142
144
|
onRename: (workSession: L2WorkSessionListItem) => void
|
|
145
|
+
syncingWorkIds: readonly string[]
|
|
146
|
+
onReloadChat: (workSession: L2WorkSessionListItem) => void
|
|
143
147
|
onArrangementChange: (arrangement: L2WorkSessionArrangement, movedWorkId: string) => void
|
|
144
148
|
}
|
|
145
149
|
|
|
@@ -343,7 +347,9 @@ const SortableWorkSessionRow = memo(function SortableWorkSessionRow({
|
|
|
343
347
|
onSelect,
|
|
344
348
|
onTogglePin,
|
|
345
349
|
onDelete,
|
|
346
|
-
onRename
|
|
350
|
+
onRename,
|
|
351
|
+
syncing,
|
|
352
|
+
onReloadChat
|
|
347
353
|
}: {
|
|
348
354
|
workSession: L2WorkSessionListItem
|
|
349
355
|
mobile: boolean
|
|
@@ -357,6 +363,8 @@ const SortableWorkSessionRow = memo(function SortableWorkSessionRow({
|
|
|
357
363
|
onTogglePin: (workId: string) => void
|
|
358
364
|
onDelete: (workId: string) => void
|
|
359
365
|
onRename: (workSession: L2WorkSessionListItem) => void
|
|
366
|
+
syncing: boolean
|
|
367
|
+
onReloadChat: (workSession: L2WorkSessionListItem) => void
|
|
360
368
|
}): React.JSX.Element {
|
|
361
369
|
const suppressTouchClick = useRef(false)
|
|
362
370
|
const lastPointer = useRef('')
|
|
@@ -606,6 +614,14 @@ const SortableWorkSessionRow = memo(function SortableWorkSessionRow({
|
|
|
606
614
|
<PencilIcon />
|
|
607
615
|
{t('renameSessionTitle')}
|
|
608
616
|
</ContextMenuItem>
|
|
617
|
+
<ContextMenuItem
|
|
618
|
+
data-testid={`work-session-reload-chat-${workSession.workId}`}
|
|
619
|
+
disabled={actionsDisabled || deleting || syncing}
|
|
620
|
+
onClick={() => onReloadChat(workSession)}
|
|
621
|
+
>
|
|
622
|
+
{syncing ? <LoaderCircleIcon className="animate-spin" /> : <RefreshCwIcon />}
|
|
623
|
+
{t('reloadChatData')}
|
|
624
|
+
</ContextMenuItem>
|
|
609
625
|
<ContextMenuItem
|
|
610
626
|
data-testid={`work-session-copy-project-path-${workSession.workId}`}
|
|
611
627
|
onClick={copyProjectPath}
|
|
@@ -666,6 +682,7 @@ function L2WorkSessionSidebarComponent({
|
|
|
666
682
|
onModeChange,
|
|
667
683
|
onRefreshFiles,
|
|
668
684
|
onPreviewProject,
|
|
685
|
+
onPreviewProjectWindow,
|
|
669
686
|
onOpenTerminal,
|
|
670
687
|
onCreate,
|
|
671
688
|
onReplace,
|
|
@@ -675,6 +692,8 @@ function L2WorkSessionSidebarComponent({
|
|
|
675
692
|
onTogglePin,
|
|
676
693
|
onDelete,
|
|
677
694
|
onRename,
|
|
695
|
+
syncingWorkIds,
|
|
696
|
+
onReloadChat,
|
|
678
697
|
onArrangementChange
|
|
679
698
|
}: L2WorkSessionSidebarProps): React.JSX.Element {
|
|
680
699
|
const { t } = useTranslation('workbench')
|
|
@@ -753,14 +772,6 @@ function L2WorkSessionSidebarComponent({
|
|
|
753
772
|
]
|
|
754
773
|
: mode === 'files'
|
|
755
774
|
? [
|
|
756
|
-
{
|
|
757
|
-
key: 'new-terminal',
|
|
758
|
-
label: t('newTerminal'),
|
|
759
|
-
disabled,
|
|
760
|
-
icon: <TerminalIcon />,
|
|
761
|
-
testId: 'project-files-terminal-open',
|
|
762
|
-
onSelect: onOpenTerminal
|
|
763
|
-
},
|
|
764
775
|
{
|
|
765
776
|
key: 'preview-project',
|
|
766
777
|
label: t('projectPreview'),
|
|
@@ -769,6 +780,22 @@ function L2WorkSessionSidebarComponent({
|
|
|
769
780
|
testId: 'project-preview-open',
|
|
770
781
|
onSelect: onPreviewProject
|
|
771
782
|
},
|
|
783
|
+
{
|
|
784
|
+
key: 'preview-project-window',
|
|
785
|
+
label: t('projectPreviewWindow'),
|
|
786
|
+
disabled: !canBrowseFiles,
|
|
787
|
+
icon: <ExternalLinkIcon />,
|
|
788
|
+
testId: 'project-preview-window-open',
|
|
789
|
+
onSelect: onPreviewProjectWindow
|
|
790
|
+
},
|
|
791
|
+
{
|
|
792
|
+
key: 'new-terminal',
|
|
793
|
+
label: t('newTerminal'),
|
|
794
|
+
disabled,
|
|
795
|
+
icon: <TerminalIcon />,
|
|
796
|
+
testId: 'project-files-terminal-open',
|
|
797
|
+
onSelect: onOpenTerminal
|
|
798
|
+
},
|
|
772
799
|
{
|
|
773
800
|
key: 'refresh-files',
|
|
774
801
|
label: t('refreshFiles'),
|
|
@@ -888,6 +915,8 @@ function L2WorkSessionSidebarComponent({
|
|
|
888
915
|
onTogglePin={onTogglePin}
|
|
889
916
|
onDelete={onDelete}
|
|
890
917
|
onRename={onRename}
|
|
918
|
+
syncing={syncingWorkIds.includes(workSession.workId)}
|
|
919
|
+
onReloadChat={onReloadChat}
|
|
891
920
|
/>
|
|
892
921
|
)
|
|
893
922
|
})
|
|
@@ -89,6 +89,7 @@ import type { L3CapabilityModeNames } from '@common/l3_modules/capability-modes/
|
|
|
89
89
|
import { L2WorkbenchCapabilityModePicker } from './l2-workbench-capability-mode-picker'
|
|
90
90
|
import { useL2NativeUi } from './native-ui/l2-native-ui'
|
|
91
91
|
import { L2NativeUiExtras, L2NativeUiQuestions } from './native-ui/l2-native-ui-view'
|
|
92
|
+
import type { L2WorkbenchPluginWidgetSlots } from './l2-workbench-plugin-slots'
|
|
92
93
|
|
|
93
94
|
interface L2WorkbenchTaskSlotInput {
|
|
94
95
|
source: L2ChatSource
|
|
@@ -107,13 +108,15 @@ export interface L2WorkbenchComposerSlots {
|
|
|
107
108
|
}) => ReactNode
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
interface L2WorkbenchChatComposerProps
|
|
111
|
+
interface L2WorkbenchChatComposerProps
|
|
112
|
+
extends L2WorkbenchComposerSlots, L2WorkbenchPluginWidgetSlots {
|
|
111
113
|
onOpenSettings?: (target?: 'model-presets') => void
|
|
112
114
|
workSession: L2WorkSessionListItem
|
|
113
115
|
chatRuntime: L2WorkbenchChatRuntime
|
|
114
116
|
inputRuntime: L2WorkbenchChatInputRuntime
|
|
115
117
|
gitRuntime: L2GitWorkspaceRuntime
|
|
116
118
|
active: boolean
|
|
119
|
+
widgetsVisible: boolean
|
|
117
120
|
mobile: boolean
|
|
118
121
|
invokePluginMethod: L2WorkbenchBiz['invokePluginMethod']
|
|
119
122
|
listPiCommands: L2WorkbenchBiz['listPiCommands']
|
|
@@ -161,8 +164,10 @@ function L2WorkbenchChatComposerComponent({
|
|
|
161
164
|
renderTaskSummary,
|
|
162
165
|
renderTaskMenu,
|
|
163
166
|
renderPluginComposerPanel,
|
|
167
|
+
renderPluginWidgets,
|
|
164
168
|
gitRuntime,
|
|
165
169
|
active,
|
|
170
|
+
widgetsVisible,
|
|
166
171
|
mobile,
|
|
167
172
|
invokePluginMethod,
|
|
168
173
|
listPiCommands,
|
|
@@ -633,6 +638,9 @@ function L2WorkbenchChatComposerComponent({
|
|
|
633
638
|
<p className="mb-1 px-1 text-xs text-muted-foreground">{t('nativeUiComposer')}</p>
|
|
634
639
|
) : null}
|
|
635
640
|
|
|
641
|
+
{widgetsVisible && !basicChat
|
|
642
|
+
? renderPluginWidgets({ source, placement: 'aboveEditor' })
|
|
643
|
+
: null}
|
|
636
644
|
<form
|
|
637
645
|
aria-label={
|
|
638
646
|
workSessionRunning
|
|
@@ -1021,6 +1029,9 @@ function L2WorkbenchChatComposerComponent({
|
|
|
1021
1029
|
</PromptInputFooter>
|
|
1022
1030
|
</InputGroup>
|
|
1023
1031
|
</form>
|
|
1032
|
+
{widgetsVisible && !basicChat
|
|
1033
|
+
? renderPluginWidgets({ source, placement: 'belowEditor' })
|
|
1034
|
+
: null}
|
|
1024
1035
|
<L2NativeUiExtras snapshot={nativeUi.state.snapshot} placement="belowEditor" />
|
|
1025
1036
|
|
|
1026
1037
|
{activePluginPanel &&
|
|
@@ -73,6 +73,7 @@ export interface L2WorkbenchChatRuntime {
|
|
|
73
73
|
) => Promise<L2ChatMessageDetail>
|
|
74
74
|
compact: (source: L2ChatSource) => Promise<void>
|
|
75
75
|
reload: (source: L2ChatSource, mode?: L2ChatMode) => Promise<void>
|
|
76
|
+
reloadMessages: (source: L2ChatSource) => Promise<void>
|
|
76
77
|
setPresentation: (source: L2ChatSource, mode: L2ChatMode) => Promise<void>
|
|
77
78
|
disconnected: () => void
|
|
78
79
|
dispose: () => void
|
|
@@ -611,6 +612,50 @@ export function createL2WorkbenchChatRuntime(
|
|
|
611
612
|
)
|
|
612
613
|
},
|
|
613
614
|
|
|
615
|
+
async reloadMessages(sourceInput): Promise<void> {
|
|
616
|
+
const source = L2ChatSourceSchema.parse(sourceInput)
|
|
617
|
+
const key = sourceKey(source)
|
|
618
|
+
const workSession = currentWorkSessions.get(source.workId)
|
|
619
|
+
if (!workSession || !sameSource(sourceFor(workSession), source)) {
|
|
620
|
+
throw new Error(l2WorkbenchText('reloadChatSourceChanged'))
|
|
621
|
+
}
|
|
622
|
+
const previous = statesBySource.get(key)
|
|
623
|
+
if (
|
|
624
|
+
!workSessionsReady ||
|
|
625
|
+
disposed ||
|
|
626
|
+
pendingSyncs.has(key) ||
|
|
627
|
+
previous?.syncStatus === 'syncing'
|
|
628
|
+
) {
|
|
629
|
+
throw new Error(l2WorkbenchText('sourceSyncing'))
|
|
630
|
+
}
|
|
631
|
+
const epoch = connectionEpoch
|
|
632
|
+
const queue = eventQueues.get(key)
|
|
633
|
+
const previousSyncEpoch = queue?.syncEpoch ?? null
|
|
634
|
+
if (queue) queue.syncEpoch = null
|
|
635
|
+
subscriptionPool.set(source.workId, source)
|
|
636
|
+
setSourceState(source, { ...(previous ?? EMPTY_CHAT_SOURCE_STATE), syncStatus: 'syncing' })
|
|
637
|
+
try {
|
|
638
|
+
// 重拉期间继续接收旧基线上的实时消息,失败时无需丢弃缓存或补偿重放。
|
|
639
|
+
await appSocket.request(L2ChatSocketContracts.subscribe, {
|
|
640
|
+
subscriptions: [{ source, cursor: null }]
|
|
641
|
+
})
|
|
642
|
+
} catch (error) {
|
|
643
|
+
if (!disposed && epoch === connectionEpoch && isCurrentAndSubscribed(source)) {
|
|
644
|
+
if (queue && eventQueues.get(key) === queue) queue.syncEpoch = previousSyncEpoch
|
|
645
|
+
const current = statesBySource.get(key)
|
|
646
|
+
if (current)
|
|
647
|
+
setSourceState(source, { ...current, syncStatus: previous?.syncStatus ?? 'loading' })
|
|
648
|
+
}
|
|
649
|
+
console.error('[Pi Desk][WorkbenchChat] 重新加载聊天数据失败,保留已有缓存', {
|
|
650
|
+
workId: source.workId,
|
|
651
|
+
sessionId: source.sessionId,
|
|
652
|
+
branchId: source.branchId,
|
|
653
|
+
message: error instanceof Error ? error.message : String(error)
|
|
654
|
+
})
|
|
655
|
+
throw error
|
|
656
|
+
}
|
|
657
|
+
},
|
|
658
|
+
|
|
614
659
|
async setPresentation(sourceInput, mode): Promise<void> {
|
|
615
660
|
const source = L2ChatSourceSchema.parse(sourceInput)
|
|
616
661
|
const key = sourceKey(source)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { CheckIcon, CopyIcon, LoaderCircleIcon, RefreshCwIcon } from 'lucide-react'
|
|
3
|
+
import { CheckIcon, CircleAlertIcon, CopyIcon, LoaderCircleIcon, RefreshCwIcon } from 'lucide-react'
|
|
4
4
|
import { useTranslation } from 'react-i18next'
|
|
5
|
+
import { cn } from '@client/l4_foundation/lib/l4-utils'
|
|
6
|
+
import styles from './l2-work-session-columns.module.css'
|
|
5
7
|
import { Button } from '@client/l4_foundation/ui/shadcn/button'
|
|
6
8
|
import {
|
|
7
9
|
Popover,
|
|
@@ -36,11 +38,14 @@ export function L2WorkbenchInitializationNotice({
|
|
|
36
38
|
<Button
|
|
37
39
|
variant="ghost"
|
|
38
40
|
size="sm"
|
|
39
|
-
|
|
41
|
+
aria-label={t('initializationDegraded')}
|
|
42
|
+
title={t('initializationDegraded')}
|
|
43
|
+
className={cn(styles.headerAction, 'text-xs text-destructive hover:text-destructive')}
|
|
40
44
|
/>
|
|
41
45
|
}
|
|
42
46
|
>
|
|
43
|
-
{
|
|
47
|
+
<CircleAlertIcon className={cn(styles.headerActionIcon, 'size-4')} aria-hidden="true" />
|
|
48
|
+
<span className={styles.headerActionLabel}>{t('initializationDegraded')}</span>
|
|
44
49
|
</PopoverTrigger>
|
|
45
50
|
<PopoverContent
|
|
46
51
|
align="end"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import type { PluginSource } from '@jetcrab/pi-desk-sdk/browser'
|
|
3
|
+
|
|
4
|
+
export interface L2WorkbenchPluginWidgetSlots {
|
|
5
|
+
renderPluginWidgets: (input: {
|
|
6
|
+
source: PluginSource
|
|
7
|
+
placement: 'aboveEditor' | 'belowEditor'
|
|
8
|
+
}) => ReactNode
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface L2WorkbenchPluginSessionSlots extends L2WorkbenchPluginWidgetSlots {
|
|
12
|
+
renderPluginSessionHeader: (input: { source: PluginSource; title: ReactNode }) => ReactNode
|
|
13
|
+
}
|
|
@@ -66,6 +66,7 @@ import { L4_APP_VERSION } from '@client/l4_foundation/l4-app-info'
|
|
|
66
66
|
import { L2WorkbenchSidebarLayout } from './l2-workbench-sidebar-layout'
|
|
67
67
|
import type { L4PluginBrowserContributionDescriptor } from '@client/l4_foundation/plugin-host/l4-plugin-host-runtime'
|
|
68
68
|
import type { L2WorkbenchComposerSlots } from './l2-workbench-chat-composer'
|
|
69
|
+
import type { L2WorkbenchPluginSessionSlots } from './l2-workbench-plugin-slots'
|
|
69
70
|
import { createL2WorkbenchBiz } from './l2-workbench-biz'
|
|
70
71
|
import { createL2WorkbenchVersionRefresh } from './l2-workbench-version-refresh'
|
|
71
72
|
import { L2FileWorkspaceRuntime } from './file-workspace/l2-file-workspace-runtime'
|
|
@@ -112,6 +113,7 @@ import {
|
|
|
112
113
|
} from './l2-work-session-sidebar-order'
|
|
113
114
|
import { useL2WorkbenchBranchPicker } from './hooks/l2-workbench-branch-picker'
|
|
114
115
|
import { useL2WorkSessionRename } from './hooks/l2-work-session-rename'
|
|
116
|
+
import { useL2WorkbenchChatReload } from './hooks/l2-workbench-chat-reload'
|
|
115
117
|
import { L2WorkSessionRenameDialog } from './views/l2-work-session-rename-dialog'
|
|
116
118
|
import { useL2WorkbenchPluginHealth } from './hooks/l2-workbench-plugin-health'
|
|
117
119
|
import { useL2WorkbenchSessionHistory } from './hooks/l2-workbench-session-history'
|
|
@@ -171,10 +173,11 @@ function WorkbenchDisplaySlot<Input>({
|
|
|
171
173
|
return render(input)
|
|
172
174
|
}
|
|
173
175
|
|
|
174
|
-
interface L2WorkbenchProps extends L2WorkbenchComposerSlots {
|
|
176
|
+
interface L2WorkbenchProps extends L2WorkbenchComposerSlots, L2WorkbenchPluginSessionSlots {
|
|
175
177
|
onTerminalReady: () => Promise<void>
|
|
176
178
|
onTerminalDisconnected: () => void
|
|
177
179
|
onOpenTerminal: (cwd?: string) => Promise<void>
|
|
180
|
+
onOpenProjectPreviewWindow: (cwd: string) => void
|
|
178
181
|
renderTerminalWorkspace: (input: { mobile: boolean; children: ReactNode }) => ReactNode
|
|
179
182
|
listPluginManagement: () => Promise<L2PluginManagementSnapshot>
|
|
180
183
|
subscribePluginManagementChanges: (listener: () => void) => () => void
|
|
@@ -220,6 +223,7 @@ export function L2Workbench({
|
|
|
220
223
|
onTerminalReady,
|
|
221
224
|
onTerminalDisconnected,
|
|
222
225
|
onOpenTerminal,
|
|
226
|
+
onOpenProjectPreviewWindow,
|
|
223
227
|
renderTerminalWorkspace,
|
|
224
228
|
listPluginManagement,
|
|
225
229
|
subscribePluginManagementChanges,
|
|
@@ -233,7 +237,9 @@ export function L2Workbench({
|
|
|
233
237
|
renderSettings,
|
|
234
238
|
renderTaskSummary,
|
|
235
239
|
renderTaskMenu,
|
|
236
|
-
renderPluginComposerPanel
|
|
240
|
+
renderPluginComposerPanel,
|
|
241
|
+
renderPluginWidgets,
|
|
242
|
+
renderPluginSessionHeader
|
|
237
243
|
}: L2WorkbenchProps): React.JSX.Element {
|
|
238
244
|
const { t } = useTranslation('workbench')
|
|
239
245
|
const { clientId, appSocket } = useL4AppSocket()
|
|
@@ -370,6 +376,7 @@ export function L2Workbench({
|
|
|
370
376
|
createL2WorkbenchChatRuntime(appSocket, (input) => workbenchBiz.getChatMessageDetail(input)),
|
|
371
377
|
[appSocket, workbenchBiz]
|
|
372
378
|
)
|
|
379
|
+
const chatReload = useL2WorkbenchChatReload(chatRuntime, workSessions)
|
|
373
380
|
const chatInputRuntime = useMemo(
|
|
374
381
|
() => createL2WorkbenchChatInputRuntime(workbenchBiz, chatRuntime),
|
|
375
382
|
[chatRuntime, workbenchBiz]
|
|
@@ -1915,6 +1922,12 @@ export function L2Workbench({
|
|
|
1915
1922
|
fileRuntime.collapseExpandedWindow()
|
|
1916
1923
|
pluginHost.previewProject(activeWorkId ? fileRuntime.getDirectory(activeWorkId) : activeCwd)
|
|
1917
1924
|
}}
|
|
1925
|
+
onPreviewProjectWindow={() => {
|
|
1926
|
+
if (!activeCwd) return
|
|
1927
|
+
onOpenProjectPreviewWindow(
|
|
1928
|
+
activeWorkId ? fileRuntime.getDirectory(activeWorkId) : activeCwd
|
|
1929
|
+
)
|
|
1930
|
+
}}
|
|
1918
1931
|
onOpenTerminal={() => {
|
|
1919
1932
|
closeMobileDrawer()
|
|
1920
1933
|
fileRuntime.collapseExpandedWindow()
|
|
@@ -1928,6 +1941,8 @@ export function L2Workbench({
|
|
|
1928
1941
|
onTogglePin={togglePinnedWorkSession}
|
|
1929
1942
|
onDelete={requestDeleteWorkSession}
|
|
1930
1943
|
onRename={renameSession.show}
|
|
1944
|
+
syncingWorkIds={chatReload.syncingWorkIds}
|
|
1945
|
+
onReloadChat={chatReload.reload}
|
|
1931
1946
|
onArrangementChange={requestArrangementUpdate}
|
|
1932
1947
|
/>
|
|
1933
1948
|
)
|
|
@@ -1969,6 +1984,8 @@ export function L2Workbench({
|
|
|
1969
1984
|
renderTaskSummary={renderTaskSummary}
|
|
1970
1985
|
renderTaskMenu={renderTaskMenu}
|
|
1971
1986
|
renderPluginComposerPanel={renderPluginComposerPanel}
|
|
1987
|
+
renderPluginWidgets={renderPluginWidgets}
|
|
1988
|
+
renderPluginSessionHeader={renderPluginSessionHeader}
|
|
1972
1989
|
gitRuntime={gitRuntime}
|
|
1973
1990
|
invokePluginMethod={workbenchBiz.invokePluginMethod}
|
|
1974
1991
|
listPiCommands={workbenchBiz.listPiCommands}
|