@jetcrab/pi-desk 1.1.20-dev → 1.1.22-dev
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 +81 -1
- package/package.json +3 -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-project-preview-biz.ts +4 -3
- 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 +19 -8
- package/src/client/l2_biz/workbench/l2-workbench-chat-composer.tsx +12 -1
- 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 +15 -2
- 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-project-document-git.ts +4 -3
- package/src/client/l3_modules/project-files/l3-project-document-view.tsx +4 -1
- package/src/client/l3_modules/project-files/l3-project-reader.ts +23 -35
- 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/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/workbench/l2-workbench-locale-messages.ts +2 -0
- package/src/server/l1_entry/node/l1-server.ts +8 -4
- 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-plugin-owner-runtime.ts +133 -13
- package/src/server/l4_foundation/pi/l4-pi-session-initialization.ts +9 -0
- 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/react-loadable-manifest.json +1 -2
- package/temp/build/pi-desk/release/.next/required-server-files.js +2 -2
- package/temp/build/pi-desk/release/.next/required-server-files.json +2 -2
- 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 +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-files/list/route.js +1 -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 -2
- package/temp/build/pi-desk/release/.next/server/app/api/project-git/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-branches/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-changes/list/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-diff/get/route.js +2 -2
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-heads/get/route.js +1 -1
- package/temp/build/pi-desk/release/.next/server/app/api/project-git-log/list/route.js +2 -2
- 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 +140 -140
- 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/25406.js +8 -8
- 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/56830.js +235 -0
- 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/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/static/MV6_lBt7Mzmi1rBNI-3dx/_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/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-e8a5274c115f528d.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/{250a6b93cc2222d1.css → 1e780a4936aa6ba3.css} +1 -1
- 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/10881.js +0 -296
- package/temp/build/pi-desk/release/.next/static/RLKcK-RQF5FvEeDmS9uih/_buildManifest.js +0 -1
- package/temp/build/pi-desk/release/.next/static/chunks/27611-0890482fe76bd9d6.js +0 -238
- package/temp/build/pi-desk/release/.next/static/chunks/31087-d8e5765d541d5c26.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/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/77592-5bef6acb8e7c4b96.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-a50c456648537afb.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-0789c43551b29dd2.js +0 -158
- package/temp/build/pi-desk/release/.next/static/chunks/webpack-d7248206b7fc7ba8.js +0 -1
- package/temp/build/pi-desk/release/.next/static/css/a4cf28fd76c70165.css +0 -1
- /package/temp/build/pi-desk/release/.next/static/{RLKcK-RQF5FvEeDmS9uih → MV6_lBt7Mzmi1rBNI-3dx}/_ssgManifest.js +0 -0
|
@@ -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
|
|
@@ -680,6 +682,7 @@ function L2WorkSessionSidebarComponent({
|
|
|
680
682
|
onModeChange,
|
|
681
683
|
onRefreshFiles,
|
|
682
684
|
onPreviewProject,
|
|
685
|
+
onPreviewProjectWindow,
|
|
683
686
|
onOpenTerminal,
|
|
684
687
|
onCreate,
|
|
685
688
|
onReplace,
|
|
@@ -769,14 +772,6 @@ function L2WorkSessionSidebarComponent({
|
|
|
769
772
|
]
|
|
770
773
|
: mode === 'files'
|
|
771
774
|
? [
|
|
772
|
-
{
|
|
773
|
-
key: 'new-terminal',
|
|
774
|
-
label: t('newTerminal'),
|
|
775
|
-
disabled,
|
|
776
|
-
icon: <TerminalIcon />,
|
|
777
|
-
testId: 'project-files-terminal-open',
|
|
778
|
-
onSelect: onOpenTerminal
|
|
779
|
-
},
|
|
780
775
|
{
|
|
781
776
|
key: 'preview-project',
|
|
782
777
|
label: t('projectPreview'),
|
|
@@ -785,6 +780,22 @@ function L2WorkSessionSidebarComponent({
|
|
|
785
780
|
testId: 'project-preview-open',
|
|
786
781
|
onSelect: onPreviewProject
|
|
787
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
|
+
},
|
|
788
799
|
{
|
|
789
800
|
key: 'refresh-files',
|
|
790
801
|
label: t('refreshFiles'),
|
|
@@ -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 &&
|
|
@@ -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'
|
|
@@ -172,10 +173,11 @@ function WorkbenchDisplaySlot<Input>({
|
|
|
172
173
|
return render(input)
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
interface L2WorkbenchProps extends L2WorkbenchComposerSlots {
|
|
176
|
+
interface L2WorkbenchProps extends L2WorkbenchComposerSlots, L2WorkbenchPluginSessionSlots {
|
|
176
177
|
onTerminalReady: () => Promise<void>
|
|
177
178
|
onTerminalDisconnected: () => void
|
|
178
179
|
onOpenTerminal: (cwd?: string) => Promise<void>
|
|
180
|
+
onOpenProjectPreviewWindow: (cwd: string) => void
|
|
179
181
|
renderTerminalWorkspace: (input: { mobile: boolean; children: ReactNode }) => ReactNode
|
|
180
182
|
listPluginManagement: () => Promise<L2PluginManagementSnapshot>
|
|
181
183
|
subscribePluginManagementChanges: (listener: () => void) => () => void
|
|
@@ -221,6 +223,7 @@ export function L2Workbench({
|
|
|
221
223
|
onTerminalReady,
|
|
222
224
|
onTerminalDisconnected,
|
|
223
225
|
onOpenTerminal,
|
|
226
|
+
onOpenProjectPreviewWindow,
|
|
224
227
|
renderTerminalWorkspace,
|
|
225
228
|
listPluginManagement,
|
|
226
229
|
subscribePluginManagementChanges,
|
|
@@ -234,7 +237,9 @@ export function L2Workbench({
|
|
|
234
237
|
renderSettings,
|
|
235
238
|
renderTaskSummary,
|
|
236
239
|
renderTaskMenu,
|
|
237
|
-
renderPluginComposerPanel
|
|
240
|
+
renderPluginComposerPanel,
|
|
241
|
+
renderPluginWidgets,
|
|
242
|
+
renderPluginSessionHeader
|
|
238
243
|
}: L2WorkbenchProps): React.JSX.Element {
|
|
239
244
|
const { t } = useTranslation('workbench')
|
|
240
245
|
const { clientId, appSocket } = useL4AppSocket()
|
|
@@ -1917,6 +1922,12 @@ export function L2Workbench({
|
|
|
1917
1922
|
fileRuntime.collapseExpandedWindow()
|
|
1918
1923
|
pluginHost.previewProject(activeWorkId ? fileRuntime.getDirectory(activeWorkId) : activeCwd)
|
|
1919
1924
|
}}
|
|
1925
|
+
onPreviewProjectWindow={() => {
|
|
1926
|
+
if (!activeCwd) return
|
|
1927
|
+
onOpenProjectPreviewWindow(
|
|
1928
|
+
activeWorkId ? fileRuntime.getDirectory(activeWorkId) : activeCwd
|
|
1929
|
+
)
|
|
1930
|
+
}}
|
|
1920
1931
|
onOpenTerminal={() => {
|
|
1921
1932
|
closeMobileDrawer()
|
|
1922
1933
|
fileRuntime.collapseExpandedWindow()
|
|
@@ -1973,6 +1984,8 @@ export function L2Workbench({
|
|
|
1973
1984
|
renderTaskSummary={renderTaskSummary}
|
|
1974
1985
|
renderTaskMenu={renderTaskMenu}
|
|
1975
1986
|
renderPluginComposerPanel={renderPluginComposerPanel}
|
|
1987
|
+
renderPluginWidgets={renderPluginWidgets}
|
|
1988
|
+
renderPluginSessionHeader={renderPluginSessionHeader}
|
|
1976
1989
|
gitRuntime={gitRuntime}
|
|
1977
1990
|
invokePluginMethod={workbenchBiz.invokePluginMethod}
|
|
1978
1991
|
listPiCommands={workbenchBiz.listPiCommands}
|
|
@@ -157,9 +157,11 @@ export function positionL3ConversationMessage(
|
|
|
157
157
|
offset,
|
|
158
158
|
addTailSpace
|
|
159
159
|
})
|
|
160
|
-
clearL3ConversationTailSpacer(scrollElement)
|
|
161
|
-
|
|
162
160
|
const turn = addTailSpace ? logicalTailTurn(scrollElement) : null
|
|
161
|
+
// 同一轮的定位空间可直接重算,先清空会让浏览器截短 scrollTop。
|
|
162
|
+
if (!turn || activeTailTurn(scrollElement) !== turn) {
|
|
163
|
+
clearL3ConversationTailSpacer(scrollElement)
|
|
164
|
+
}
|
|
163
165
|
if (!turn) {
|
|
164
166
|
scrollElement.scrollTop = Math.max(
|
|
165
167
|
0,
|
|
@@ -177,11 +179,15 @@ export function positionL3ConversationMessage(
|
|
|
177
179
|
}
|
|
178
180
|
|
|
179
181
|
const { targetScrollTop, minHeight } = measureTailPosition(scrollElement, target, turn, offset)
|
|
180
|
-
|
|
181
|
-
turn.
|
|
182
|
+
const nextMinHeight = `${Math.ceil(minHeight)}px`
|
|
183
|
+
if (turn.style.minHeight !== nextMinHeight) turn.style.minHeight = nextMinHeight
|
|
184
|
+
if (!turn.hasAttribute('data-chat-tail-space')) turn.dataset.chatTailSpace = ''
|
|
182
185
|
const anchor = target.dataset.chatScrollAnchor
|
|
183
|
-
if (
|
|
184
|
-
|
|
186
|
+
if (turn.dataset.chatTailAnchor !== anchor) {
|
|
187
|
+
if (anchor) turn.dataset.chatTailAnchor = anchor
|
|
188
|
+
else delete turn.dataset.chatTailAnchor
|
|
189
|
+
}
|
|
190
|
+
if (turn.dataset.chatTailOffset !== String(offset)) turn.dataset.chatTailOffset = String(offset)
|
|
185
191
|
scrollElement.scrollTop = targetScrollTop
|
|
186
192
|
logL3ConversationScroll('position:end', scrollElement, {
|
|
187
193
|
target: target.dataset.chatScrollAnchor ?? null,
|
|
@@ -10,7 +10,8 @@ export function useL3ProjectDocumentGit(
|
|
|
10
10
|
git: L3ProjectGitBrowser,
|
|
11
11
|
host: RefObject<HTMLDivElement | null>,
|
|
12
12
|
savePosition: () => void,
|
|
13
|
-
active: boolean
|
|
13
|
+
active: boolean,
|
|
14
|
+
refreshOnFocus = true
|
|
14
15
|
): { refresh: () => void; unavailable: boolean } {
|
|
15
16
|
useSyncExternalStore(git.subscribe, git.getRevision, git.getRevision)
|
|
16
17
|
const snapshot = git.getWorkspace(runtime.cwd)?.snapshot
|
|
@@ -29,7 +30,7 @@ export function useL3ProjectDocumentGit(
|
|
|
29
30
|
}, [diffActive, git, path, runtime])
|
|
30
31
|
|
|
31
32
|
useEffect(() => {
|
|
32
|
-
if (!active || !path || diffActive) return
|
|
33
|
+
if (!refreshOnFocus || !active || !path || diffActive) return
|
|
33
34
|
const visible = (): boolean =>
|
|
34
35
|
window.document.visibilityState === 'visible' &&
|
|
35
36
|
Boolean(host.current?.getClientRects().length) &&
|
|
@@ -54,7 +55,7 @@ export function useL3ProjectDocumentGit(
|
|
|
54
55
|
window.removeEventListener('focus', onFocus)
|
|
55
56
|
window.document.removeEventListener('visibilitychange', onFocus)
|
|
56
57
|
}
|
|
57
|
-
}, [active, diffActive, git, host, path, refresh, runtime])
|
|
58
|
+
}, [active, diffActive, git, host, path, refresh, refreshOnFocus, runtime])
|
|
58
59
|
|
|
59
60
|
useEffect(() => {
|
|
60
61
|
if (!active || !path || diffActive || document?.status !== 'text') return
|
|
@@ -53,6 +53,7 @@ export function L3ProjectDocumentView({
|
|
|
53
53
|
gitRuntime,
|
|
54
54
|
compact,
|
|
55
55
|
visible = true,
|
|
56
|
+
refreshOnFocus = true,
|
|
56
57
|
onEscape,
|
|
57
58
|
onSwitchPane
|
|
58
59
|
}: {
|
|
@@ -61,6 +62,7 @@ export function L3ProjectDocumentView({
|
|
|
61
62
|
gitRuntime: L3ProjectGitBrowser
|
|
62
63
|
compact: boolean
|
|
63
64
|
visible?: boolean
|
|
65
|
+
refreshOnFocus?: boolean
|
|
64
66
|
onEscape: () => void
|
|
65
67
|
onSwitchPane?: () => void
|
|
66
68
|
}): React.JSX.Element {
|
|
@@ -107,7 +109,8 @@ export function L3ProjectDocumentView({
|
|
|
107
109
|
gitRuntime,
|
|
108
110
|
hostRef,
|
|
109
111
|
savePosition,
|
|
110
|
-
visible
|
|
112
|
+
visible,
|
|
113
|
+
refreshOnFocus
|
|
111
114
|
)
|
|
112
115
|
const comparison = document?.status === 'text' ? document.comparison : undefined
|
|
113
116
|
const [computed, setComputed] = useState<{
|
|
@@ -85,49 +85,37 @@ export interface L3ProjectReader extends L3ProjectFileReader {
|
|
|
85
85
|
|
|
86
86
|
export function createL3ProjectReader(
|
|
87
87
|
clientId: string,
|
|
88
|
-
context: contract.L3ProjectReadContext
|
|
88
|
+
context: contract.L3ProjectReadContext,
|
|
89
|
+
onUnauthorized?: () => void
|
|
89
90
|
): L3ProjectReader {
|
|
91
|
+
async function request<I, O>(
|
|
92
|
+
api: { path: string; request: z.ZodType<I>; response: z.ZodType<O> },
|
|
93
|
+
input: I,
|
|
94
|
+
signal?: AbortSignal
|
|
95
|
+
): Promise<O> {
|
|
96
|
+
try {
|
|
97
|
+
return await requestL3Project(clientId, api, input, signal)
|
|
98
|
+
} catch (cause) {
|
|
99
|
+
if (!signal?.aborted && cause instanceof L3ProjectRequestError && cause.status === 401)
|
|
100
|
+
onUnauthorized?.()
|
|
101
|
+
throw cause
|
|
102
|
+
}
|
|
103
|
+
}
|
|
90
104
|
return {
|
|
91
|
-
list: (path, signal) =>
|
|
92
|
-
requestL3Project(clientId, contract.L3ProjectFileList, { ...context, path }, signal),
|
|
105
|
+
list: (path, signal) => request(contract.L3ProjectFileList, { ...context, path }, signal),
|
|
93
106
|
search: (input, signal) =>
|
|
94
|
-
|
|
107
|
+
request(contract.L3ProjectFileSearch, { ...context, ...input }, signal),
|
|
95
108
|
get: (path, imagePreviewMode, signal) =>
|
|
96
|
-
|
|
97
|
-
clientId,
|
|
98
|
-
contract.L3ProjectFileGet,
|
|
99
|
-
{ ...context, path, imagePreviewMode },
|
|
100
|
-
signal
|
|
101
|
-
),
|
|
109
|
+
request(contract.L3ProjectFileGet, { ...context, path, imagePreviewMode }, signal),
|
|
102
110
|
snapshot: (signal, refresh) =>
|
|
103
|
-
|
|
111
|
+
request(contract.L3ProjectGitGet, { ...context, refresh }, signal),
|
|
104
112
|
branches: (repositoryRoot, signal) =>
|
|
105
|
-
|
|
106
|
-
clientId,
|
|
107
|
-
contract.L3ProjectGitBranches,
|
|
108
|
-
{ ...context, repositoryRoot },
|
|
109
|
-
signal
|
|
110
|
-
),
|
|
113
|
+
request(contract.L3ProjectGitBranches, { ...context, repositoryRoot }, signal),
|
|
111
114
|
log: (repositoryRoot, query, signal) =>
|
|
112
|
-
|
|
113
|
-
clientId,
|
|
114
|
-
contract.L3ProjectGitLog,
|
|
115
|
-
{ ...context, repositoryRoot, ...query },
|
|
116
|
-
signal
|
|
117
|
-
),
|
|
115
|
+
request(contract.L3ProjectGitLog, { ...context, repositoryRoot, ...query }, signal),
|
|
118
116
|
changes: (repositoryRoot, query, signal) =>
|
|
119
|
-
|
|
120
|
-
clientId,
|
|
121
|
-
contract.L3ProjectGitChanges,
|
|
122
|
-
{ ...context, repositoryRoot, ...query },
|
|
123
|
-
signal
|
|
124
|
-
),
|
|
117
|
+
request(contract.L3ProjectGitChanges, { ...context, repositoryRoot, ...query }, signal),
|
|
125
118
|
diff: (repositoryRoot, path, signal, comparison) =>
|
|
126
|
-
|
|
127
|
-
clientId,
|
|
128
|
-
contract.L3ProjectGitDiff,
|
|
129
|
-
{ ...context, repositoryRoot, path, comparison },
|
|
130
|
-
signal
|
|
131
|
-
)
|
|
119
|
+
request(contract.L3ProjectGitDiff, { ...context, repositoryRoot, path, comparison }, signal)
|
|
132
120
|
}
|
|
133
121
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext } from 'react'
|
|
4
|
+
|
|
5
|
+
export const L4ClientIdContext = createContext<string | null>(null)
|
|
6
|
+
|
|
7
|
+
export function useL4ClientId(): string {
|
|
8
|
+
const clientId = useContext(L4ClientIdContext)
|
|
9
|
+
if (clientId === null) throw new Error('缺少页面 clientId Provider')
|
|
10
|
+
return clientId
|
|
11
|
+
}
|