From be1764e0166c5228af19c3cc17c8dd2be69ada20 Mon Sep 17 00:00:00 2001 From: inman Date: Mon, 7 Sep 2026 10:06:37 +0800 Subject: [PATCH] feat: move plugin workspace into project settings --- ...4e2__project-settings-plugin-navigation.md | 86 +++++++++++++++ ...-move-plugins-project-settings-7c91a4e2.md | 100 ++++++++++++++++++ README.md | 2 +- scripts/lib/pi-product-artifact.mjs | 2 +- src/App.tsx | 10 +- src/components/layout/MainLayout.tsx | 1 + src/components/layout/Sidebar.tsx | 18 ---- src/lib/ai-modules.ts | 1 + src/pages/Plugins/index.tsx | 57 +++++++--- src/pages/Plugins/legacy-plugin-redirect.tsx | 21 +++- src/pages/ProjectConfiguration/index.tsx | 32 +++++- tests/e2e/plugin-marketplace.spec.ts | 17 ++- tests/e2e/project-plugins.spec.ts | 8 +- tests/unit/login-page.test.tsx | 6 +- tests/unit/main-layout-module-gate.test.tsx | 4 +- tests/unit/pi-product-artifact.test.ts | 6 +- tests/unit/plugin-navigation.test.tsx | 38 +++++-- .../unit/plugin-workspace-controller.test.ts | 4 +- tests/unit/plugins-page.test.tsx | 26 +++-- 19 files changed, 363 insertions(+), 76 deletions(-) create mode 100644 .project-docs/10-decisions/proposals/20260906-move-plugins-project-settings-7c91a4e2__project-settings-plugin-navigation.md create mode 100644 .project-docs/30-worklog/tasks/20260906-move-plugins-project-settings-7c91a4e2.md diff --git a/.project-docs/10-decisions/proposals/20260906-move-plugins-project-settings-7c91a4e2__project-settings-plugin-navigation.md b/.project-docs/10-decisions/proposals/20260906-move-plugins-project-settings-7c91a4e2__project-settings-plugin-navigation.md new file mode 100644 index 0000000..1258205 --- /dev/null +++ b/.project-docs/10-decisions/proposals/20260906-move-plugins-project-settings-7c91a4e2__project-settings-plugin-navigation.md @@ -0,0 +1,86 @@ +# Project Configuration owns Plugin navigation + +## Status + +Proposed for integration; implemented and verified in task +`20260906-move-plugins-project-settings-7c91a4e2`. + +## Date + +2026-09-06 + +## Context + +The integrated product exposed one reviewed unified Plugin workspace at +`/plugins` through a standalone Code sidebar entry. Project Configuration no +longer contained its predecessor Plugin Services sheet. The user first directed +that Plugins belong in Project Settings, then clarified that the workspace must +live on the same project page used to configure Agents and Skills rather than +replace that page after navigation. + +The navigation change must not collapse the existing independent lifecycles for +Marketplace acquisition, device installation, project enablement, Agent Skill +assignment, runtime authorization, and billing. It also must not restore the +retired duplicate sheet or move Main-owned Plugin authority into Renderer code. + +## Decision + +- The Agent/Skill Project Configuration page is the visible owner of the single + unified Plugin workspace. +- Its `插件` resource card opens the workspace in a wide same-page drawer. The + Project Configuration page remains mounted behind it. +- `/project-config/plugins` is the canonical deep link for the drawer-open state; + it renders Project Configuration rather than a standalone Plugin page. +- `/plugins` preserves query and navigation state while redirecting to the + canonical URL. `/plugin-marketplace`, `/my-plugins`, and `/project-plugins` + remain deterministic filtered compatibility redirects. +- The Code sidebar has no standalone Plugin entry. +- The canonical workspace remains accessible before Code Provider initialization + and when no project is active, so Marketplace and account/device views do not + incorrectly depend on local project selection. +- The existing unified controller, data sources, mutation semantics, + authentication, and lifecycle separation remain unchanged. + +## Rationale + +Plugin installation and activation configure resources that projects may use, so +Project Configuration is the clearer navigation owner. Reusing the unified +workspace avoids competing product surfaces and preserves one authoritative view +across Marketplace, Account Library, device packages, and project status. +Compatibility redirects protect existing deep links and authenticated return +flows while establishing an unambiguous canonical location. + +## Consequences + +- Project Configuration gains a Plugin card beside models, Skills, and knowledge, + with access retained in the no-active-project state. +- Closing the Plugin drawer returns to the already-mounted Project Configuration + page; Agent assignment actions close the drawer to expose that same page. +- Existing bookmarks continue to work, but tests and new product links should use + `/project-config/plugins`. +- Canonical project memory that names the Code sidebar or `/plugins` as the sole + current entry must be superseded during Integration Mode. +- No backend contract, package trust boundary, runtime permission, billing rule, + or native Web Search behavior changes. + +## Supersedes + +- The navigation-only claim that the Code sidebar owns the single `插件` entry and + `/plugins` is the canonical product route. +- It does not supersede the retirement of the duplicate Project Configuration + Plugin Services sheet; that implementation remains retired. + +## Evidence + +- User direction: “插件应该在项目设置中”, clarified as “是放在配置agent、skill那个项目页”. +- Focused unit verification: 62/62 passed. +- Electron Plugin journeys: 4/4 passed. +- Full unit, typecheck, lint, and Vite production build passed. + +## Related + +- `.project-docs/10-decisions/adr-008-interactive-ai-app-scaffold.md` +- `.project-docs/30-worklog/tasks/20260906-move-plugins-project-settings-7c91a4e2.md` +- `src/pages/ProjectConfiguration/index.tsx` +- `src/pages/Plugins/index.tsx` +- `src/pages/Plugins/legacy-plugin-redirect.tsx` diff --git a/.project-docs/30-worklog/tasks/20260906-move-plugins-project-settings-7c91a4e2.md b/.project-docs/30-worklog/tasks/20260906-move-plugins-project-settings-7c91a4e2.md new file mode 100644 index 0000000..7b110b5 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260906-move-plugins-project-settings-7c91a4e2.md @@ -0,0 +1,100 @@ +# Task: Move plugins into project settings + +## Identity + +- Task ID: 20260906-move-plugins-project-settings-7c91a4e2 +- Mode: Feature +- Branch: codex/20260906-move-plugins-project-settings-7c91a4e2-move-plugins-project-settings +- Worktree: /Users/inmanx/Documents/makelore-task-move-plugins-project-settings-7c91a4e2 +- Base commit: f8eee430f4f4167dc2ecc682ece96c652cdde14b +- Owner: codex +- Status: Ready for integration + +## Scope + +- Move the single user-visible Plugin workspace from the Code sidebar directly + into the same Project Configuration page used to configure Agents, Skills, + models, and knowledge. +- Make `/project-config/plugins` the canonical Plugin URL while retaining + deterministic compatibility redirects from `/plugins`, `/plugin-marketplace`, + `/my-plugins`, and `/project-plugins`. +- Update focused unit and Electron E2E coverage, the packaged-product marker, and + README current-state wording for the navigation change. + +## Intent And Constraints + +- Preserve the existing unified Plugin workspace and its projections of + Marketplace, Account Library, official-device state, Device Packages, current + project state, and retained IDs; do not restore the retired duplicate Plugin + Services sheet. +- Keep acquisition, device installation, project enablement, Agent Skill + assignment, runtime authorization, and billing as separate lifecycles. +- Preserve Main-owned Host API, authentication, package verification, and runtime + authority. Renderer navigation must not introduce direct IPC or local-runtime + HTTP access. +- Keep Project Configuration and its nested Plugin route available before Code + Provider initialization, including the no-active-project state. +- Render the nested route as a same-page Project Configuration drawer, not as a + replacement standalone Plugin page. +- Preserve legacy deep-link query/state and authenticated return paths. +- Keep the single Makelore light visual system and Chinese product copy. + +## Outcome + +- Added a `插件` ResourceCard alongside models, Skills, and knowledge in Project + Configuration, retained access in the no-active-project state, and removed the + standalone Code sidebar Plugin entry. +- Changed `/project-config/plugins` to render the Project Configuration page with + its wide Plugin drawer open. The Agent/Skill project page remains mounted behind + the drawer; closing it returns to the same page. Query/state-preserving + `/plugins` compatibility and existing filtered legacy redirects remain intact. +- Reused the unified Plugin controller and view inside the drawer without + restoring the retired duplicate Plugin Services implementation. +- Kept the unified Plugin controller, data sources, mutations, authentication, + and lifecycle semantics unchanged. +- Updated initialization gates, login return path, README, packaged Marketplace + artifact assertion, focused unit tests, and Electron E2E journeys. + +## Verification + +- Focused navigation, layout, embedded Plugin, controller, login, and artifact + verification — 6 files, 62 tests passed; the embedded-view regression failed + before implementation and passed afterward. +- `corepack pnpm run typecheck` — passed. +- Scoped ESLint across all changed source and test files — passed with no output. +- `corepack pnpm run build:vite` — Renderer, Main, Preload, and utility builds passed; + only existing Browserslist, mixed-import, and chunk-size warnings remained. +- `node ./node_modules/@playwright/test/cli.js test tests/e2e/project-plugins.spec.ts tests/e2e/plugin-marketplace.spec.ts` — 4/4 passed, including assertions that the Project Configuration page remains mounted behind the Plugin drawer. +- `corepack pnpm test` — 221 regular files (1861 passed, 3 skipped) and the pressure suite (1 passed). +- `corepack pnpm run lint:check` — passed with 0 errors and 5 pre-existing warnings. +- `git diff --check` — passed. + +## Follow-ups + +- Integration should reconcile the navigation wording in canonical project memory + and review overlap with planning task `20260906-remove-project-gate-7c31` before + merging. No product-semantic conflict was found in the current peer-task audit. + +## Promotion Candidates + +- Target: `.project-docs/30-worklog/current-state.md`, + `.project-docs/20-architecture/system-overview.md`, and + `.project-docs/40-domain/business-rules.md`. +- Proposal: record the Agent/Skill Project Configuration page as the visible + owner of the single unified Plugin workspace. Its `插件` card opens a same-page + wide drawer; `/project-config/plugins` is the canonical deep link for that + state, and old public URLs remain deterministic compatibility redirects. This + rehomes the reviewed unified workspace and does not restore the superseded + duplicate Plugin Services implementation. +- Evidence: explicit user direction in this task, focused unit coverage, 4/4 + Electron E2E, full unit/typecheck/lint/build verification, and the task-owned + proposal `20260906-move-plugins-project-settings-7c91a4e2__project-settings-plugin-navigation.md`. +- Future impact: new Plugin navigation must originate in Project Configuration, + while Plugin lifecycle boundaries, Web Search exclusion, and Main-owned + authority remain unchanged. +- Semantic conflict: this supersedes canonical statements that the Code sidebar + owns the only Plugin entry and `/plugins` is canonical; it does not conflict + with ADR-008 or the Plugin lifecycle boundaries. +- Human confirmation: already supplied and clarified by the user: Plugins belong + on the same project page used to configure Agents and Skills, not on a separate + page. No additional product-direction approval is required during integration. diff --git a/README.md b/README.md index 75e6478..edd61ab 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Makelore 是一个面向软件、视觉创作与智能机器人的 AI 桌面工 - AI 绘画生成:服务端从已确认规格编译专业图片或视频指令并返回不可变 Quote;客户端只展示媒介、画幅、数量、格式、时长、警告和设计点,不展示或改写供应商 Prompt、模型、价格原子或存储地址。确认时只提交 Quote 身份。图片可使用当前 Workspace 的上传素材或生成作品,视频可绑定已审核首帧;任务与资产始终属于 Workspace。 - AI 绘画健壮性:Renderer 为每次命令生成稳定 operation id,网络结果未知时保留原命令供原样重试,不把未知写入当失败或创建第二次生成。Main 负责 Token 刷新、Agent Gateway REST 提交和有界 Run 查询,并将可恢复事件流投影为 Host API SSE;断线后按事件游标续接并重新读取权威 Workspace。Canvas 只使用 Works Square 云端 V2 契约,没有本地语义适配器或降级路径,上游不可用时明确报错。 - AI 绘画项目栏只展示 Workspace,不再在项目下创建独立设计会话。删除时必须完整输入项目名称;删除后项目、Living Form、任务、参考图和生成作品会从账户中隐藏且无法访问,不影响用户已另存到磁盘的副本。删除当前项目后自动打开最近更新的剩余项目,删除最后一个项目后进入空状态。 -- 插件工作台:Code 侧栏只有一个“插件”入口,`/plugins` 在同一列表中投影官方目录、账号 Library、本机 Device Packages 与当前项目状态;旧 Marketplace、My Plugins、Project Plugins URL 只做确定性筛选重定向。来源身份保持 `official:`、`local:`、`retained:` 分离;“免费获取”、官方设备交付、项目启用和伙伴分配仍分别写入既有 authority,不会自动推进下一步。任一来源失败不会清空其他来源;本机 Skill/Pi 插件仍只能通过对话安装,页面只管理已安装包并明确其本机全局生效范围。 +- 项目插件设置:Code 侧栏不再单列“插件”;配置 Agent、Skill、模型与知识的项目配置页内提供“插件”卡片,并在同页宽抽屉中投影官方目录、账号 Library、本机 Device Packages 与当前项目状态。深链使用 `/project-config/plugins` 打开该项目页抽屉;旧 `/plugins`、Marketplace、My Plugins、Project Plugins URL 只做保留查询条件的确定性重定向。来源身份保持 `official:`、`local:`、`retained:` 分离;“免费获取”、官方设备交付、项目启用和伙伴分配仍分别写入既有 authority,不会自动推进下一步。任一来源失败不会清空其他来源;本机 Skill/Pi 插件仍只能通过对话安装,页面只管理已安装包并明确其本机全局生效范围。 - 官方插件运行架构:Renderer 只调用 Main-owned Marketplace facade;Main 负责账号、请求 deadline、签名/摘要校验、不可变 Release、current selection 与原子回滚。Pi parent worker 使用冻结的 effective snapshot,将每个有效 Skill 与已验证 Package Store root 成对传给 resource loader、Extension Host 和 CLI;普通 `skill_only` 下载包不依赖运行时 Policy,也不得携带可执行脚本。Project Scaffold 的 `.mjs` 是代码所有的 bundled 例外,只能从签名客户端固定资源根通过应用自带 Node 执行,不能扩展为 Marketplace 下载 artifact 的脚本权限。正式激活仍等待官方 Ed25519 公钥(production key activation HOLD);生产私钥只能来自部署 secret,测试使用注入的临时密钥。 - 原生 Web Search 是冻结 selected model/provider/credential 的 parent-only 模型工具;只有 Works `model_capabilities` 明确声明支持时才出现,使用当前模型的正常计费链,不依赖 Marketplace 获取、Release、Admission、Hosted Provider 或 `agent_browser` 回退。 - 本机 Device Packages 只由 Conversation 工具发起安装,支持 npm、Git、绝对本地 Plugin 目录和 loose `SKILL.md`。Main 在预览和独立确认后提交不可变本机 generation,禁用生命周期脚本;确认界面明确提示 Pi extension 以桌面当前用户权限执行。新建/空闲 parent 会同时加载所有显式安装且当前启用的 Skill,以及必需 Makelore 扩展之外的全部 Pi extension,active parent 在 turn settled 后切换,child 不继承这些资源,也不会扫描 ambient 项目或用户目录;Renderer 不提供安装源选择器。 diff --git a/scripts/lib/pi-product-artifact.mjs b/scripts/lib/pi-product-artifact.mjs index 021e2fd..4c2977e 100644 --- a/scripts/lib/pi-product-artifact.mjs +++ b/scripts/lib/pi-product-artifact.mjs @@ -69,7 +69,7 @@ const MARKETPLACE_ARTIFACT_MARKERS = Object.freeze({ '/api/coding/plugin-marketplace/library', '免费获取', '获取、项目启用与伙伴分配彼此独立', - '/plugins', + '/project-config/plugins', ]), }); const FORBIDDEN_PROVIDER_AUTHORITY_MARKERS = Object.freeze([ diff --git a/src/App.tsx b/src/App.tsx index f1cd4a1..6c520ec 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -26,14 +26,17 @@ import { subscribeHostEvent } from '@/lib/host-events'; import { reportDesktopActivity, type DesktopActivityModule } from '@/lib/host-api'; import { installRendererPerformanceDiagnostics } from '@/lib/performance-diagnostics'; import { resolveSupportedLanguage } from '../shared/language'; -import { LegacyPluginRedirect } from './pages/Plugins/legacy-plugin-redirect'; +import { + LegacyPluginRedirect, + PluginWorkspaceRedirect, + PROJECT_SETTINGS_PLUGINS_PATH, +} from './pages/Plugins/legacy-plugin-redirect'; const Models = lazy(() => import('./pages/Models').then(({ Models: component }) => ({ default: component }))); const Chat = lazy(() => import('./pages/Chat').then(({ Chat: component }) => ({ default: component }))); const Home = lazy(() => import('./pages/Home').then(({ Home: component }) => ({ default: component }))); const PreviewScene = lazy(() => import('./pages/Makelore').then(({ PreviewScene: component }) => ({ default: component }))); const ProjectConfiguration = lazy(() => import('./pages/ProjectConfiguration').then(({ ProjectConfiguration: component }) => ({ default: component }))); -const Plugins = lazy(() => import('./pages/Plugins').then(({ Plugins: component }) => ({ default: component }))); const Workbench = lazy(() => import('./pages/Workbench').then(({ Workbench: component }) => ({ default: component }))); const ImageCanvas = lazy(() => import('./pages/ImageCanvas').then(({ ImageCanvas: component }) => ({ default: component }))); const ImagePromptMuseum = lazy(() => import('./pages/ImagePromptMuseum').then(({ ImagePromptMuseum: component }) => ({ default: component }))); @@ -442,7 +445,8 @@ function App() { )} > } /> - } /> + } /> + } /> } /> } /> } /> diff --git a/src/components/layout/MainLayout.tsx b/src/components/layout/MainLayout.tsx index 06c2037..73a2ab6 100644 --- a/src/components/layout/MainLayout.tsx +++ b/src/components/layout/MainLayout.tsx @@ -38,6 +38,7 @@ export function MainLayout() { const isPromptMuseum = location.pathname === '/image-prompts' || location.pathname.startsWith('/image-prompts/'); const isChatWorkspace = location.pathname === '/chat'; const isInitializationSafeRoute = location.pathname === '/project-config' + || location.pathname.startsWith('/project-config/') || location.pathname === '/plugins' || !isProgrammingModule; diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index f5e9b8b..f868717 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -12,7 +12,6 @@ import { FolderKanban, LogOut, Plus, - Puzzle, Settings as SettingsIcon, UserCircle2, } from 'lucide-react'; @@ -584,23 +583,6 @@ export function Sidebar({ workspaceLayout = false, sidebarPeekOpen = false, onSi {!sidebarCollapsed && '新建项目'} -
- -
+
+ ) :
+
+ {props.onBackToProjectSettings ? ( + + ) : null} +
+

项目配置

+

插件

+

+ MakeLore 运营发布官方插件;本机 Skill 和 Pi extension 仅通过对话安装。获取、项目启用与伙伴分配彼此独立,这里集中展示账号、设备、当前项目与伙伴状态。 +

+
-
+ } -
+