@jetcrab/pi-desk 1.1.20-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 +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/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 +3 -3
- package/temp/build/pi-desk/release/.next/required-server-files.json +3 -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 +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/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/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/{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 → 8Yd33M0zPi4tbRn4xCaRY}/_ssgManifest.js +0 -0
package/docs/plugins.md
CHANGED
|
@@ -50,6 +50,8 @@ Node Entry 默认导出普通 `PiDeskPluginDefinition` 对象即可,`definePiD
|
|
|
50
50
|
|
|
51
51
|
`registerBrowserEntry()` 最多一次,路径位于包内,文件名为 `entry.js`。无需自有资源时 `setup()` 不必返回清理;创建 Timer、Watcher、连接或子进程时返回 disposer,释放旧实例资源。Host 自动撤销注册;旧异步清理不得写入新实例。
|
|
52
52
|
|
|
53
|
+
宿主启动时,单个 Node 插件的入口导入、`setup()` 和 Browser 资源检查共用 15 秒上限。超时不代表插件代码已经停止;宿主撤销旧实例,等首轮插件加载、旧加载及其清理结束后,延迟 10 秒自动重试一次。明确加载错误和再次超时不自动重试;手动重载、禁用、卸载或宿主关闭会取消待执行的恢复。加载失败可在插件管理行点击“重新加载”,只重载本机代码。
|
|
54
|
+
|
|
53
55
|
会话能力在真实原生扩展中绑定,不用 Node Global 冒充 Session:
|
|
54
56
|
|
|
55
57
|
```ts
|
|
@@ -81,7 +83,7 @@ plugin.addPreviewDirectory(directory, { default: true })
|
|
|
81
83
|
|
|
82
84
|
## Browser Entry 与懒界面
|
|
83
85
|
|
|
84
|
-
Entry
|
|
86
|
+
Entry 默认导出普通同步函数即可;在函数返回前完成全部注册。`registerContribution(kind, name, definition)` 支持 `application`、`composer-panel`、`settings-page`、`session-sidebar-tab`、`message-view`;输入区挂件与会话标题项分别使用 `registerWidget()`、`registerSessionHeaderItem()`,不能通过 `registerContribution` 注册。Entry 只放元数据、loader、轻量页面状态和事件订阅;返回 disposer 清理自有订阅与资源。
|
|
85
87
|
|
|
86
88
|
Entry 不静态引入 React、ReactDOM、UI、CSS 或大型实现,也不等网络或动态 import 后再注册。UI 放在 `load()` 的动态模块,导出 `{ mount({ container, target, host, signal }) }`;Lazy Module 顶层不启动副作用。
|
|
87
89
|
|
|
@@ -107,6 +109,84 @@ Application 使用 `chrome: 'host'` 让宿主负责 Dialog、标题、关闭、
|
|
|
107
109
|
|
|
108
110
|
界面继承宿主明暗主题与字号,布局使用 `rem`、合理内边距和自身滚动;窄屏与触控可用,不以裁切或只靠 hover 隐藏必要操作。按钮、表单、加载/错误/重试优先复用 `/react/base`;需要 Markdown 时才加载 `/react/markdown`。
|
|
109
111
|
|
|
112
|
+
### 输入框上下方挂件
|
|
113
|
+
|
|
114
|
+
在 Browser Entry 注册,不调用原生 `ctx.ui.setWidget()`:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
plugin.registerWidget('session-info', {
|
|
118
|
+
placement: 'aboveEditor',
|
|
119
|
+
load: () => import('./session-info.js').then((module) => module.default)
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
同目录 `session-info.js` 是最小 Lazy View:
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
export default {
|
|
127
|
+
mount({ container, target }) {
|
|
128
|
+
const element = document.createElement('p')
|
|
129
|
+
element.textContent = `当前会话:${target.source.sessionId}`
|
|
130
|
+
element.style.cssText = 'margin:0;overflow-wrap:anywhere'
|
|
131
|
+
container.append(element)
|
|
132
|
+
return () => element.remove()
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`placement` 只允许 `aboveEditor / belowEditor`,省略时是上方。Target 为 `{ kind: 'widget', source }`。挂件在对应聊天区可见时挂载,自然高度;没有内容时不预留空白。多个挂件按插件名和注册名稳定排列,不进入标题栏更多浮层。关闭或隐藏聊天区、Source 变化和插件重载会 Abort/dispose;重新挂载时按现有 Method/Push 建立数据基线。
|
|
138
|
+
|
|
139
|
+
### 锚定浮层
|
|
140
|
+
|
|
141
|
+
Lazy View 可从 `@jetcrab/pi-desk-sdk/react/base` 使用受控 `PluginPopover`。`trigger` 接收 React Element,其他内容作为 children;宿主组件负责 Portal、锚定、视口约束和焦点,打开不会抢走输入焦点。悬停、短暂提示和业务状态由插件控制,不自行定位到宿主 DOM。
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
const [open, setOpen] = useState(false)
|
|
145
|
+
|
|
146
|
+
<PluginPopover
|
|
147
|
+
open={open}
|
|
148
|
+
onOpenChange={setOpen}
|
|
149
|
+
side="top"
|
|
150
|
+
trigger={<PluginButton>查看详情</PluginButton>}
|
|
151
|
+
>
|
|
152
|
+
<div style={{ padding: '0.75rem' }}>当前内容</div>
|
|
153
|
+
</PluginPopover>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`side` 默认 `bottom`,`align` 默认 `start`,`className` 用于自身浮层的布局。浮层维持指定方向并限制可用高度;长内容自行使用 `PluginScroll`。调用方关闭或卸载时清理自己的计时器、Observer 和订阅。此组件需要支持该导出的宿主 Host Runtime,不能只更新插件依赖后向旧宿主请求。
|
|
157
|
+
|
|
158
|
+
### 会话标题右侧条目
|
|
159
|
+
|
|
160
|
+
`inline` 和 `overflow` 必须分别声明,可以独立选择普通按钮或自定义视图:
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
const showSession = {
|
|
164
|
+
type: 'action',
|
|
165
|
+
icon: { type: 'builtin', name: 'file' },
|
|
166
|
+
label: '会话信息',
|
|
167
|
+
onSelect({ source, host }) {
|
|
168
|
+
host.notify({ level: 'info', title: '当前会话', description: source.sessionId })
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
plugin.registerSessionHeaderItem('session-info', {
|
|
173
|
+
inline: {
|
|
174
|
+
type: 'custom',
|
|
175
|
+
minWidthRem: 6,
|
|
176
|
+
load: () => import('./session-info.js').then((module) => module.default)
|
|
177
|
+
},
|
|
178
|
+
overflow: showSession
|
|
179
|
+
})
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
- `action` 只接受固定 `icon`、`label`、`onSelect({ source, host, signal })`,不提供动态图标、文字或状态订阅。宿主显示图标加文字,异步操作期间防止重复点击;失败记录局部错误,失效后的回调不得继续使用旧 Host。信号属于该展示生命周期,不会回滚服务端已经产生的副作用。
|
|
183
|
+
- `custom` 只提供 `load()`,宿主不包装点击回调。inline custom 必须额外声明正数 `minWidthRem`,单位为 `rem`;宿主按此预留可用宽度,不通过重复挂载业务视图测量。自定义内容应适应获得的容器,不能靠超宽 DOM 挤占标题。overflow custom 不声明宽度。
|
|
184
|
+
- 自定义 Target 为 `{ kind: 'session-header-item', source, presentation: 'inline' | 'overflow' }`。两种表现可以使用不同模块,也可以复用同一个 `{ mount }`;loader 分别按表现缓存和重试。可复用上面的 `session-info.js`,或使用公共 React/UI 构建交互视图。
|
|
185
|
+
- 标题项按插件名和注册名稳定排列;宽度不足时从末尾收进更多,宿主保留标题及必要操作。普通按钮选择后关闭更多;自定义区域内点击不会被转发,也不会自动关闭更多。Escape 或外部点击关闭浮层,键盘与触控均可操作。
|
|
186
|
+
- 收纳会卸载 inline 自定义视图;只有更多打开时才挂载 overflow 自定义视图,关闭后释放。动态状态与运行/停止等复杂交互由自定义视图自行实现;业务状态归插件运行态,不归 mount,也不能只存于会被卸载的组件局部状态。卸载只清理视图资源,不停止项目进程。
|
|
187
|
+
|
|
188
|
+
两种注册均限定 Browser Entry 同步阶段,名称规则与其他贡献相同。它们需要支持对应方法的宿主和 SDK;旧宿主缺少能力时禁用这项入口,不使用 `registerContribution` 别名或访问宿主 DOM 代替。现有 `composer-panel` 仍是人工打开的会话弹窗,不是上下方挂件。
|
|
189
|
+
|
|
110
190
|
### 侧栏顶部操作
|
|
111
191
|
|
|
112
192
|
`session-sidebar-tab` 的 Lazy `mount({ target, host })` 内可为当前 Tab 设置顶部右侧按钮:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jetcrab/pi-desk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.21",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"viewerjs": "^1.11.8",
|
|
71
71
|
"ws": "^8.21.3",
|
|
72
72
|
"zod": "^4.4.3",
|
|
73
|
-
"@jetcrab/pi-desk-sdk": "^1.1.
|
|
73
|
+
"@jetcrab/pi-desk-sdk": "^1.1.8"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@earendil-works/pi-agent-core": "1.2.0",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"build:plugin-host-runtime": "pnpm --filter @jetcrab/pi-desk-sdk build",
|
|
113
113
|
"start": "node bin/pi-desk.js --no-open",
|
|
114
114
|
"test:package": "node --test tests/l4-pi-desk-package.test.mjs",
|
|
115
|
-
"test:pidesk": "node --import tsx --test tests/l4-pidesk-command.test.ts tests/l2-plugin-commands.test.ts tests/l4-pidesk-session-reload.test.ts tests/l4-pidesk-runtime.test.ts tests/l4-pidesk-runtime-bundle.test.mjs tests/l4-pidesk-docs.test.mjs tests/l4-pidesk-installed-runners.test.mjs tests/l4-pidesk-package-maintenance.test.ts tests/l4-pidesk-release-gate.test.mjs tests/l4-pi-desk-process-tree.test.mjs tests/l4-pi-runner-process.test.ts tests/l4-pi-global-runtime.test.mjs tests/l4-pi-desk-supervisor-cli.test.mjs",
|
|
115
|
+
"test:pidesk": "node --import tsx --test --test-concurrency=1 tests/l4-pidesk-command.test.ts tests/l2-plugin-commands.test.ts tests/l4-pidesk-session-reload.test.ts tests/l4-pidesk-runtime.test.ts tests/l4-pidesk-runtime-bundle.test.mjs tests/l4-pidesk-docs.test.mjs tests/l4-pidesk-installed-runners.test.mjs tests/l4-pidesk-package-maintenance.test.ts tests/l4-pidesk-release-gate.test.mjs tests/l4-pi-desk-process-tree.test.mjs tests/l4-pi-runner-process.test.ts tests/l4-pi-global-runtime.test.mjs tests/l4-pi-desk-supervisor-cli.test.mjs",
|
|
116
116
|
"test:app-websocket": "pnpm build && node --import tsx --test tests/l2-chat-state.test.ts tests/l2-workbench-chat-repository.test.ts tests/l2-workbench-chat.test.ts tests/l2-workbench-layout.test.ts tests/l2-work-session-column-layout.test.ts tests/l2-work-session-update.test.ts tests/l1-app-websocket.test.mjs",
|
|
117
117
|
"lint": "eslint .",
|
|
118
118
|
"lint:production": "eslint . --ignore-pattern \"apps/desktop/**\" --ignore-pattern \"**/tests/**\" --ignore-pattern \"**/__tests__/**\" --ignore-pattern \"**/*.test.*\" --ignore-pattern \"**/*.spec.*\"",
|
package/src/app/page.tsx
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { L1HomePage } from '@client/l1_entry/pages/l1-home-page'
|
|
2
|
+
import { L1AppRuntimeProvider } from '@client/l1_entry/providers/l1-app-runtime-provider'
|
|
2
3
|
|
|
3
4
|
export default function Home(): React.JSX.Element {
|
|
4
|
-
return
|
|
5
|
+
return (
|
|
6
|
+
<L1AppRuntimeProvider>
|
|
7
|
+
<L1HomePage />
|
|
8
|
+
</L1AppRuntimeProvider>
|
|
9
|
+
)
|
|
5
10
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, type ComponentProps } from 'react'
|
|
4
|
+
import { buildL2ProjectPreviewHref } from '@common/l2_biz/project-preview/l2-project-preview-navigation'
|
|
4
5
|
import { L2TerminalBiz } from '@client/l2_biz/terminal/l2-terminal-biz'
|
|
5
6
|
import { L2TerminalWorkspace } from '@client/l2_biz/terminal/l2-terminal-workspace'
|
|
6
7
|
import { useL4PluginHost } from '@client/l4_foundation/plugin-host/l4-plugin-host-context'
|
|
@@ -12,6 +13,8 @@ import { L2PluginManagement } from '@client/l2_biz/plugin-host/l2-plugin-managem
|
|
|
12
13
|
import { createL2PluginManagementBiz } from '@client/l2_biz/plugin-host/l2-plugin-management-biz'
|
|
13
14
|
import { L2PluginSessionSidebarTab } from '@client/l2_biz/plugin-host/l2-plugin-session-sidebar-tab'
|
|
14
15
|
import { L2PluginSettingsPage } from '@client/l2_biz/plugin-host/l2-plugin-settings-page'
|
|
16
|
+
import { L2PluginWidgets } from '@client/l2_biz/plugin-host/l2-plugin-widgets'
|
|
17
|
+
import { L2PluginSessionHeader } from '@client/l2_biz/plugin-host/l2-plugin-session-header'
|
|
15
18
|
import { L2Settings } from '@client/l2_biz/settings/l2-settings'
|
|
16
19
|
import { L2PiSettings } from '@client/l2_biz/pi-settings/l2-pi-settings'
|
|
17
20
|
import { createL2TaskCenterBiz } from '@client/l2_biz/task-center/l2-task-center-biz'
|
|
@@ -61,6 +64,28 @@ function renderPluginComposerPanel(
|
|
|
61
64
|
)
|
|
62
65
|
}
|
|
63
66
|
|
|
67
|
+
function renderPluginWidgets(
|
|
68
|
+
input: Parameters<WorkbenchProps['renderPluginWidgets']>[0]
|
|
69
|
+
): React.JSX.Element {
|
|
70
|
+
return (
|
|
71
|
+
<L2PluginWidgets
|
|
72
|
+
key={`${input.source.workId}:${input.source.sessionId}:${input.source.branchId}:${input.placement}`}
|
|
73
|
+
{...input}
|
|
74
|
+
/>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function renderPluginSessionHeader(
|
|
79
|
+
input: Parameters<WorkbenchProps['renderPluginSessionHeader']>[0]
|
|
80
|
+
): React.JSX.Element {
|
|
81
|
+
return (
|
|
82
|
+
<L2PluginSessionHeader
|
|
83
|
+
key={`${input.source.workId}:${input.source.sessionId}:${input.source.branchId}`}
|
|
84
|
+
{...input}
|
|
85
|
+
/>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
|
|
64
89
|
function renderPluginSettingsPage(
|
|
65
90
|
input: Parameters<SettingsProps['renderPluginSettingsPage']>[0]
|
|
66
91
|
): React.JSX.Element {
|
|
@@ -158,6 +183,9 @@ export function L1HomePage(): React.JSX.Element {
|
|
|
158
183
|
onTerminalReady={terminalBiz.connected}
|
|
159
184
|
onTerminalDisconnected={terminalBiz.disconnected}
|
|
160
185
|
onOpenTerminal={terminalBiz.open}
|
|
186
|
+
onOpenProjectPreviewWindow={(cwd) => {
|
|
187
|
+
window.open(buildL2ProjectPreviewHref(cwd), '_blank', 'noopener,noreferrer')
|
|
188
|
+
}}
|
|
161
189
|
renderTerminalWorkspace={renderTerminalWorkspace}
|
|
162
190
|
listPluginManagement={pluginManagementBiz.list}
|
|
163
191
|
subscribePluginManagementChanges={pluginManagementBiz.subscribeChanges}
|
|
@@ -169,6 +197,8 @@ export function L1HomePage(): React.JSX.Element {
|
|
|
169
197
|
renderPluginApplication={renderPluginApplication}
|
|
170
198
|
renderPluginSessionSidebarTab={renderPluginSessionSidebarTab}
|
|
171
199
|
renderPluginComposerPanel={renderPluginComposerPanel}
|
|
200
|
+
renderPluginWidgets={renderPluginWidgets}
|
|
201
|
+
renderPluginSessionHeader={renderPluginSessionHeader}
|
|
172
202
|
renderSettings={renderSettings}
|
|
173
203
|
renderTaskSummary={renderTaskSummary}
|
|
174
204
|
renderTaskMenu={renderTaskMenu}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef } from 'react'
|
|
4
|
+
import { useRouter, useSearchParams } from 'next/navigation'
|
|
5
|
+
import { L2BuildLoginHref } from '@common/l2_biz/auth/l2-auth-return-to'
|
|
6
|
+
import { buildL2ProjectPreviewHref } from '@common/l2_biz/project-preview/l2-project-preview-navigation'
|
|
7
|
+
import { useL4ClientId } from '@client/l4_foundation/lib/l4-client-id'
|
|
8
|
+
import { L2ProjectPreviewPage } from '@client/l2_biz/project-preview/l2-project-preview-page'
|
|
9
|
+
|
|
10
|
+
export function L1ProjectPreviewPage(): React.JSX.Element {
|
|
11
|
+
const clientId = useL4ClientId()
|
|
12
|
+
const params = useSearchParams()
|
|
13
|
+
const router = useRouter()
|
|
14
|
+
const cwd = params.get('cwd')?.trim() || null
|
|
15
|
+
const redirecting = useRef(false)
|
|
16
|
+
const onUnauthorized = useCallback((): void => {
|
|
17
|
+
if (redirecting.current) return
|
|
18
|
+
redirecting.current = true
|
|
19
|
+
window.location.assign(L2BuildLoginHref(window.location.pathname + window.location.search))
|
|
20
|
+
}, [])
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const name = cwd?.replaceAll('\\', '/').split('/').filter(Boolean).at(-1)
|
|
23
|
+
document.title = name ? `${name} · Pi Desk` : 'Pi Desk'
|
|
24
|
+
}, [cwd])
|
|
25
|
+
return (
|
|
26
|
+
<L2ProjectPreviewPage
|
|
27
|
+
clientId={clientId}
|
|
28
|
+
cwd={cwd}
|
|
29
|
+
onUnauthorized={onUnauthorized}
|
|
30
|
+
onSelect={(directory) => router.push(buildL2ProjectPreviewHref(directory))}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import type
|
|
3
|
+
import { useState, type ReactNode } from 'react'
|
|
4
4
|
import type { HostSettingsSnapshot } from '@jetcrab/pi-desk-sdk/settings'
|
|
5
5
|
import { L2_AUTH_MESSAGES } from '@common/l2_biz/auth/l2-auth-messages'
|
|
6
6
|
import { L2_CAPABILITY_MODE_MESSAGES } from '@common/l2_biz/capability-modes/l2-capability-mode-messages'
|
|
@@ -22,7 +22,8 @@ import { L4RegionProvider } from '@client/l4_foundation/locale/l4-region-provide
|
|
|
22
22
|
import { L4RegionNotice } from '@client/l4_foundation/locale/l4-region-notice'
|
|
23
23
|
import { L4AppToastProvider } from '@client/l4_foundation/ui/l4-app-toast'
|
|
24
24
|
import { TooltipProvider } from '@client/l4_foundation/ui/shadcn/tooltip'
|
|
25
|
-
import {
|
|
25
|
+
import { createL4BrowserUuid } from '@client/l4_foundation/lib/l4-browser-uuid'
|
|
26
|
+
import { L4ClientIdContext } from '@client/l4_foundation/lib/l4-client-id'
|
|
26
27
|
|
|
27
28
|
const resources = {
|
|
28
29
|
en: {
|
|
@@ -66,20 +67,22 @@ export function L1AppProviders({
|
|
|
66
67
|
children,
|
|
67
68
|
initialSettings
|
|
68
69
|
}: L1AppProvidersProps): React.JSX.Element {
|
|
70
|
+
const [clientId] = useState(createL4BrowserUuid)
|
|
71
|
+
|
|
69
72
|
return (
|
|
70
|
-
<
|
|
71
|
-
<
|
|
72
|
-
<
|
|
73
|
-
<
|
|
74
|
-
<
|
|
75
|
-
<
|
|
76
|
-
|
|
73
|
+
<L4ClientIdContext.Provider value={clientId}>
|
|
74
|
+
<L4ThemeProvider>
|
|
75
|
+
<L4DisplaySizeProvider>
|
|
76
|
+
<L4CodePreferencesProvider>
|
|
77
|
+
<L4RegionProvider resources={resources} initialSettings={initialSettings}>
|
|
78
|
+
<L4AppToastProvider>
|
|
79
|
+
<L4RegionNotice />
|
|
77
80
|
<TooltipProvider>{children}</TooltipProvider>
|
|
78
|
-
</
|
|
79
|
-
</
|
|
80
|
-
</
|
|
81
|
-
</
|
|
82
|
-
</
|
|
83
|
-
</
|
|
81
|
+
</L4AppToastProvider>
|
|
82
|
+
</L4RegionProvider>
|
|
83
|
+
</L4CodePreferencesProvider>
|
|
84
|
+
</L4DisplaySizeProvider>
|
|
85
|
+
</L4ThemeProvider>
|
|
86
|
+
</L4ClientIdContext.Provider>
|
|
84
87
|
)
|
|
85
88
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useEffect, useRef, useState, type PropsWithChildren } from 'react'
|
|
4
4
|
import { createL2PluginHostRuntime } from '@client/l2_biz/plugin-host/l2-plugin-host-runtime'
|
|
5
5
|
import { L2ProjectPreviewDialog } from '@client/l2_biz/project-preview/l2-project-preview-dialog'
|
|
6
|
-
import {
|
|
6
|
+
import { useL4ClientId } from '@client/l4_foundation/lib/l4-client-id'
|
|
7
7
|
import { L4PluginHostContext } from '@client/l4_foundation/plugin-host/l4-plugin-host-context'
|
|
8
8
|
import type { L4PluginHostRuntime } from '@client/l4_foundation/plugin-host/l4-plugin-host-runtime'
|
|
9
9
|
import { useL4AppToast } from '@client/l4_foundation/ui/l4-app-toast'
|
|
@@ -22,8 +22,8 @@ interface L1AppRuntimeOwner {
|
|
|
22
22
|
|
|
23
23
|
export function L1AppRuntimeProvider({ children }: PropsWithChildren): React.JSX.Element {
|
|
24
24
|
const toast = useL4AppToast()
|
|
25
|
+
const clientId = useL4ClientId()
|
|
25
26
|
const [runtime] = useState<L1AppRuntimeOwner>(() => {
|
|
26
|
-
const clientId = createL4BrowserUuid()
|
|
27
27
|
const owner = createL4AppSocketClient(clientId)
|
|
28
28
|
return {
|
|
29
29
|
clientId,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { L4ApiResponse } from '@common/l4_foundation/http/l4-api-response-contract'
|
|
2
|
+
import { L2ResolveAuthReturnTo } from '@common/l2_biz/auth/l2-auth-return-to'
|
|
2
3
|
import { l4LocalizedErrorMessage } from '@client/l4_foundation/locale/l4-localized-error'
|
|
3
4
|
import {
|
|
4
5
|
L2AuthLoginRequestSchema,
|
|
@@ -44,4 +45,6 @@ export async function loginL2Auth(clientId: string, input: L2AuthLoginRequest):
|
|
|
44
45
|
)
|
|
45
46
|
}
|
|
46
47
|
L2AuthLoginResponseSchema.parse(envelope.data)
|
|
48
|
+
const returnTo = new URLSearchParams(window.location.search).get('returnTo')
|
|
49
|
+
window.location.replace(L2ResolveAuthReturnTo(returnTo))
|
|
47
50
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { ArrowRightIcon, LanguagesIcon, LoaderCircleIcon } from 'lucide-react'
|
|
4
4
|
import { useEffect, useRef, useState, type FormEvent } from 'react'
|
|
5
5
|
import { useTranslation } from 'react-i18next'
|
|
6
|
-
import {
|
|
6
|
+
import { useL4ClientId } from '@client/l4_foundation/lib/l4-client-id'
|
|
7
7
|
import { useL4Region } from '@client/l4_foundation/locale/l4-region-provider'
|
|
8
8
|
import { setL4RegionDisplayLocale } from '@client/l4_foundation/locale/l4-region-store'
|
|
9
9
|
import { Button } from '@client/l4_foundation/ui/shadcn/button'
|
|
@@ -23,7 +23,7 @@ import styles from './l2-login.module.css'
|
|
|
23
23
|
export function L2Login(): React.JSX.Element {
|
|
24
24
|
const { t } = useTranslation('auth')
|
|
25
25
|
const region = useL4Region()
|
|
26
|
-
const
|
|
26
|
+
const clientId = useL4ClientId()
|
|
27
27
|
const [password, setPassword] = useState('')
|
|
28
28
|
const [error, setError] = useState<string | null>(null)
|
|
29
29
|
const [submitting, setSubmitting] = useState(false)
|
|
@@ -43,7 +43,6 @@ export function L2Login(): React.JSX.Element {
|
|
|
43
43
|
setSubmitting(true)
|
|
44
44
|
try {
|
|
45
45
|
await loginL2Auth(clientId, { password })
|
|
46
|
-
window.location.replace('/')
|
|
47
46
|
} catch (cause) {
|
|
48
47
|
submittingRef.current = false
|
|
49
48
|
setError(cause instanceof Error ? cause.message : t('loginFailed'))
|
|
@@ -72,7 +72,9 @@ const CONTRIBUTION_LABELS: Readonly<Record<L4PluginBrowserContributionDescriptor
|
|
|
72
72
|
'composer-panel': 'contributionComposer',
|
|
73
73
|
'settings-page': 'contributionSettings',
|
|
74
74
|
'message-view': 'contributionMessage',
|
|
75
|
-
'session-sidebar-tab': 'contributionSidebar'
|
|
75
|
+
'session-sidebar-tab': 'contributionSidebar',
|
|
76
|
+
widget: 'contributionWidget',
|
|
77
|
+
'session-header-item': 'contributionSessionHeader'
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
export type L2PluginCapabilityTagTone = 'neutral' | 'prompt' | 'dynamic' | 'override'
|
|
@@ -567,6 +569,9 @@ function PromptResources({
|
|
|
567
569
|
function contributionDescription(descriptor: L4PluginBrowserContributionDescriptor): string {
|
|
568
570
|
if (descriptor.kind === 'message-view') return descriptor.viewKey
|
|
569
571
|
if (descriptor.kind === 'application') return descriptor.title
|
|
572
|
+
if (descriptor.kind === 'widget' || descriptor.kind === 'session-header-item') {
|
|
573
|
+
return descriptor.contributionName
|
|
574
|
+
}
|
|
570
575
|
return descriptor.label
|
|
571
576
|
}
|
|
572
577
|
|
|
@@ -8,7 +8,9 @@ import type {
|
|
|
8
8
|
BrowserComposerPanelTarget,
|
|
9
9
|
BrowserMessageViewTarget,
|
|
10
10
|
BrowserSessionSidebarTabTarget,
|
|
11
|
+
BrowserSessionHeaderItemTarget,
|
|
11
12
|
BrowserSettingsPageTarget,
|
|
13
|
+
BrowserWidgetTarget,
|
|
12
14
|
PluginDisposer
|
|
13
15
|
} from '@jetcrab/pi-desk-sdk/browser'
|
|
14
16
|
import {
|
|
@@ -16,11 +18,17 @@ import {
|
|
|
16
18
|
useL4PluginRegistry
|
|
17
19
|
} from '@client/l4_foundation/plugin-host/l4-plugin-host-context'
|
|
18
20
|
import { Button } from '@client/l4_foundation/ui/shadcn/button'
|
|
21
|
+
import {
|
|
22
|
+
Popover,
|
|
23
|
+
PopoverContent,
|
|
24
|
+
PopoverTitle,
|
|
25
|
+
PopoverTrigger
|
|
26
|
+
} from '@client/l4_foundation/ui/shadcn/popover'
|
|
19
27
|
import { L4ErrorDetails } from '@client/l4_foundation/ui/l4-error-details'
|
|
20
28
|
import { cn } from '@client/l4_foundation/lib/l4-utils'
|
|
21
29
|
import type { L4PluginContributionKind } from '@client/l4_foundation/plugin-host/l4-plugin-host-runtime'
|
|
22
30
|
|
|
23
|
-
type L2PluginContributionLayout = 'fill' | 'intrinsic'
|
|
31
|
+
type L2PluginContributionLayout = 'fill' | 'intrinsic' | 'inline'
|
|
24
32
|
|
|
25
33
|
interface L2PluginContributionHostProps {
|
|
26
34
|
pluginName: string
|
|
@@ -32,6 +40,8 @@ interface L2PluginContributionHostProps {
|
|
|
32
40
|
| BrowserSettingsPageTarget
|
|
33
41
|
| BrowserMessageViewTarget
|
|
34
42
|
| BrowserSessionSidebarTabTarget
|
|
43
|
+
| BrowserWidgetTarget
|
|
44
|
+
| BrowserSessionHeaderItemTarget
|
|
35
45
|
targetKey?: string
|
|
36
46
|
className?: string
|
|
37
47
|
layout?: L2PluginContributionLayout
|
|
@@ -57,6 +67,7 @@ export function L2PluginContributionHost({
|
|
|
57
67
|
const containerRef = useRef<HTMLDivElement | null>(null)
|
|
58
68
|
const targetRef = useRef(target)
|
|
59
69
|
const mountIdentity = targetKey ?? target
|
|
70
|
+
const presentation = target.kind === 'session-header-item' ? target.presentation : undefined
|
|
60
71
|
const [attempt, setAttempt] = useState(0)
|
|
61
72
|
const [state, setState] = useState<MountState>({ status: 'loading' })
|
|
62
73
|
|
|
@@ -86,7 +97,9 @@ export function L2PluginContributionHost({
|
|
|
86
97
|
mountContainer.className =
|
|
87
98
|
layout === 'intrinsic'
|
|
88
99
|
? 'inline-block min-h-0 min-w-0 align-top'
|
|
89
|
-
:
|
|
100
|
+
: layout === 'inline'
|
|
101
|
+
? 'min-w-0 w-full'
|
|
102
|
+
: 'h-full min-h-0 w-full min-w-0'
|
|
90
103
|
hostContainer.append(mountContainer)
|
|
91
104
|
return runtime.mountContribution({
|
|
92
105
|
pluginName,
|
|
@@ -163,13 +176,18 @@ export function L2PluginContributionHost({
|
|
|
163
176
|
})
|
|
164
177
|
return
|
|
165
178
|
}
|
|
166
|
-
const loadState = runtime.getContributionLoadState(
|
|
179
|
+
const loadState = runtime.getContributionLoadState(
|
|
180
|
+
pluginName,
|
|
181
|
+
kind,
|
|
182
|
+
contributionName,
|
|
183
|
+
presentation
|
|
184
|
+
)
|
|
167
185
|
if (loadState.status !== 'failed') {
|
|
168
186
|
setAttempt((current) => current + 1)
|
|
169
187
|
return
|
|
170
188
|
}
|
|
171
189
|
setState({ status: 'loading' })
|
|
172
|
-
void runtime.retryContribution(pluginName, kind, contributionName).then(
|
|
190
|
+
void runtime.retryContribution(pluginName, kind, contributionName, presentation).then(
|
|
173
191
|
() => {
|
|
174
192
|
if (runtime.getEntryState(pluginName) === entryState) setAttempt((current) => current + 1)
|
|
175
193
|
},
|
|
@@ -199,7 +217,9 @@ export function L2PluginContributionHost({
|
|
|
199
217
|
'max-h-[calc(100dvh-7rem)] max-w-[calc(100dvw-3rem)] overflow-auto max-sm:max-h-[calc(100dvh-5rem)] max-sm:max-w-[calc(100dvw-2rem)]',
|
|
200
218
|
intrinsicFallbackClass
|
|
201
219
|
)
|
|
202
|
-
:
|
|
220
|
+
: layout === 'inline'
|
|
221
|
+
? 'w-full'
|
|
222
|
+
: 'h-full w-full',
|
|
203
223
|
className
|
|
204
224
|
)}
|
|
205
225
|
>
|
|
@@ -211,17 +231,58 @@ export function L2PluginContributionHost({
|
|
|
211
231
|
className={cn(
|
|
212
232
|
layout === 'intrinsic'
|
|
213
233
|
? 'inline-block min-h-0 min-w-0 align-top'
|
|
214
|
-
:
|
|
234
|
+
: layout === 'inline'
|
|
235
|
+
? 'w-full min-w-0'
|
|
236
|
+
: 'h-full min-h-0 w-full min-w-0',
|
|
215
237
|
state.status === 'error' && 'hidden'
|
|
216
238
|
)}
|
|
217
239
|
/>
|
|
218
240
|
{state.status === 'loading' ? (
|
|
219
|
-
<div
|
|
220
|
-
|
|
221
|
-
{
|
|
241
|
+
<div
|
|
242
|
+
role="status"
|
|
243
|
+
className={cn(
|
|
244
|
+
'flex items-center text-sm text-muted-foreground',
|
|
245
|
+
layout === 'inline'
|
|
246
|
+
? 'min-h-7 gap-1 py-1'
|
|
247
|
+
: 'absolute inset-0 justify-center bg-popover p-6'
|
|
248
|
+
)}
|
|
249
|
+
>
|
|
250
|
+
<LoaderCircleIcon className="mr-2 size-4 shrink-0 animate-spin" />
|
|
251
|
+
<span className="min-w-0 truncate">{t('viewLoading')}</span>
|
|
222
252
|
</div>
|
|
223
253
|
) : null}
|
|
224
|
-
{state.status === 'error' ? (
|
|
254
|
+
{state.status === 'error' && layout === 'inline' ? (
|
|
255
|
+
<div role="alert" className="min-w-0">
|
|
256
|
+
<Popover>
|
|
257
|
+
<PopoverTrigger
|
|
258
|
+
render={
|
|
259
|
+
<Button
|
|
260
|
+
type="button"
|
|
261
|
+
variant="ghost"
|
|
262
|
+
size="icon-sm"
|
|
263
|
+
aria-label={t('viewFailed')}
|
|
264
|
+
title={t('viewFailed')}
|
|
265
|
+
className="text-destructive"
|
|
266
|
+
/>
|
|
267
|
+
}
|
|
268
|
+
>
|
|
269
|
+
<AlertTriangleIcon className="size-4" />
|
|
270
|
+
</PopoverTrigger>
|
|
271
|
+
<PopoverContent align="start" finalFocus>
|
|
272
|
+
<PopoverTitle>{t('viewFailed')}</PopoverTitle>
|
|
273
|
+
<L4ErrorDetails error={state.error}>
|
|
274
|
+
<p className="text-xs text-muted-foreground [overflow-wrap:anywhere]">
|
|
275
|
+
{pluginName} · {kind} · {contributionName}
|
|
276
|
+
</p>
|
|
277
|
+
</L4ErrorDetails>
|
|
278
|
+
<Button type="button" variant="outline" size="sm" onClick={retry}>
|
|
279
|
+
<RefreshCwIcon />
|
|
280
|
+
{t('retry')}
|
|
281
|
+
</Button>
|
|
282
|
+
</PopoverContent>
|
|
283
|
+
</Popover>
|
|
284
|
+
</div>
|
|
285
|
+
) : state.status === 'error' ? (
|
|
225
286
|
<div
|
|
226
287
|
role="alert"
|
|
227
288
|
className={cn(
|
|
@@ -57,6 +57,15 @@ export function L2PluginManagementControls({
|
|
|
57
57
|
>
|
|
58
58
|
{t('retry')}
|
|
59
59
|
</Button>
|
|
60
|
+
) : plugin.status === 'failed' ? (
|
|
61
|
+
<Button
|
|
62
|
+
size="sm"
|
|
63
|
+
variant="outline"
|
|
64
|
+
disabled={disabled || pending}
|
|
65
|
+
onClick={() => onAction('reload')}
|
|
66
|
+
>
|
|
67
|
+
{t('reloadOne')}
|
|
68
|
+
</Button>
|
|
60
69
|
) : browserError && plugin.pluginName ? (
|
|
61
70
|
<Button
|
|
62
71
|
size="sm"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function calculateL2PluginSessionHeaderInlineCount(
|
|
2
|
+
widths: readonly number[],
|
|
3
|
+
availableWidth: number,
|
|
4
|
+
moreWidth: number,
|
|
5
|
+
gap: number
|
|
6
|
+
): number {
|
|
7
|
+
const total = widths.reduce((sum, width) => sum + width, 0) + Math.max(0, widths.length - 1) * gap
|
|
8
|
+
if (total <= availableWidth) return widths.length
|
|
9
|
+
|
|
10
|
+
let used = moreWidth
|
|
11
|
+
let count = 0
|
|
12
|
+
for (const width of widths) {
|
|
13
|
+
if (used + gap + width > availableWidth) break
|
|
14
|
+
used += gap + width
|
|
15
|
+
count += 1
|
|
16
|
+
}
|
|
17
|
+
return count
|
|
18
|
+
}
|