From caa0c99be17c3a0c981d25a8fb04441e83e2b232 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Thu, 17 Sep 2026 20:13:26 +0800 Subject: [PATCH] docs(web): follow up the review on the companion move and detail slots The maintained docs now name the four companion packages where they still credited `ui-settings-plugins`: the web-app row comment, the Plugins page README, the ui-settings README, and the client group README, which gains a row per companion. The ui-settings README states the `whileServed` lifetime and disposer ownership beside `bind`, and the JSDoc says the caller owns the disposer. The settings-page cookbook keeps the one home of the lazy-CJS build limitation for packages outside this repository, its JSX example is a tsx fence, and the architecture index and website label follow the page's title. The Agent Note records the shell page's `plugins.item` id, the two companions that use `ctx.remote` list api-remotes in `dsh.client.inject`, and the moved form model test speaks of the form. --- ...-09-17-settings-pages-as-companion-packages.i18n.yaml | 4 ++-- .../2026-09-17-settings-pages-as-companion-packages.md | 1 + ...2026-09-17-settings-pages-as-companion-packages.zh.md | 1 + docs/architecture.i18n.yaml | 4 ++-- docs/architecture.md | 2 +- docs/architecture.zh.md | 2 +- docs/cookbook/adding-a-settings-card.i18n.yaml | 4 ++-- docs/cookbook/adding-a-settings-card.md | 4 +++- docs/cookbook/adding-a-settings-card.zh.md | 4 +++- packages/bundle/web-app/cordis.patch.yml | 5 +++-- packages/client/README.i18n.yaml | 4 ++-- packages/client/README.md | 6 +++++- packages/client/README.zh.md | 6 +++++- packages/client/ui-plugin-manager/README.i18n.yaml | 4 ++-- packages/client/ui-plugin-manager/README.md | 4 ++-- packages/client/ui-plugin-manager/README.zh.md | 4 ++-- .../tests/settings-form-model.client.spec.ts | 2 +- packages/client/ui-settings-subagent/package.json | 3 ++- packages/client/ui-settings-web-search/package.json | 3 ++- packages/client/ui-settings/README.i18n.yaml | 4 ++-- packages/client/ui-settings/README.md | 9 +++++++-- packages/client/ui-settings/README.zh.md | 9 +++++++-- packages/client/ui-settings/src/client/settings-scope.ts | 4 +++- website/docs.ts | 2 +- 24 files changed, 62 insertions(+), 33 deletions(-) diff --git a/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.i18n.yaml b/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.i18n.yaml index 0dd8337ca2..665be1daa4 100644 --- a/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.i18n.yaml +++ b/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.md -2026-09-17-settings-pages-as-companion-packages.md: dc604c010787ee35522b351b643fb2691540b4f9 -2026-09-17-settings-pages-as-companion-packages.zh.md: 066b17c9760f381089a6a48b1ec6606e600531d3 +2026-09-17-settings-pages-as-companion-packages.md: de2f2897938fcc158c49bed772f399b4d9328d19 +2026-09-17-settings-pages-as-companion-packages.zh.md: 27d00faa156f0f48c6dc3995930be9ca629a732d diff --git a/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.md b/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.md index dc604c0107..de2f289793 100644 --- a/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.md +++ b/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.md @@ -33,6 +33,7 @@ The four official settings pages — shell, agent loop, Subagent, web search — - The Plugins page's Official group lists the same four pages in the same order, from four packages instead of one; their labels and forms are unchanged, so the page goldens hold. - The cookbook [adding a settings page](../../../../docs/cookbook/adding-a-settings-card.md) shows the community path and names the companion template. - The client module system's rule stands: a browser half rides the Loader row whose specifier is the bare package name, so a bundle split into subpath rows keeps every page it registers on its root row. +- The shell page's `plugins.item` id is `shell`, where the old package registered `bash`: `subject.id` on an official page is now a discriminator `plugins.detail.*` contributions key on, so the id names the capability the page edits rather than one executor family. ## Testing diff --git a/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.zh.md b/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.zh.md index 066b17c976..27d00faa15 100644 --- a/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.zh.md +++ b/.agents/notes/implemented/architecture/2026-09-17-settings-pages-as-companion-packages.zh.md @@ -33,6 +33,7 @@ - 插件页官方分组列出同样四个页面、同样顺序,来自四个包而不是一个;标签与表单未变,页面 golden 保持不变。 - 手册[添加设置页](../../../../docs/cookbook/adding-a-settings-card.zh.md)展示社区路径并点名伴生包模板。 - 客户端模块系统的规则不变:浏览器半侧挂在说明符为裸包名的 Loader 行上,因此拆成子路径行的组合包,其注册的所有页面都留在根行上。 +- 终端页在 `plugins.item` 里的 id 是 `shell`,旧包注册的是 `bash`:官方页面上的 `subject.id` 现在是 `plugins.detail.*` 贡献可以据以判断的标识,所以 id 以页面编辑的能力命名,而不是某一个执行器家族。 ## 测试 diff --git a/docs/architecture.i18n.yaml b/docs/architecture.i18n.yaml index ead9c04e54..63e1078eaa 100644 --- a/docs/architecture.i18n.yaml +++ b/docs/architecture.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/architecture.md -architecture.md: 37aaf83e37fb5cb7e3df4bd6ed034ccd42103919 -architecture.zh.md: 084fa76a045a9ae744228e8e43900f174b26ae9e +architecture.md: ebb28a79fb17ff1364b59dddfaeb5b6eb723bd1f +architecture.zh.md: 0a19671c6bb36df689585e8f5654ce721834bd49 diff --git a/docs/architecture.md b/docs/architecture.md index 37aaf83e37..ebb28a79fb 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -161,4 +161,4 @@ New behavior attaches to a documented extension point. Changing the loop itself | Store sessions in a new backend | implement `SessionPersistence` (`create`/`open`/`stat`/`list`/`export`) over the shared handle scaffolding | | Scope a registration to one agent | use that agent's `agent.ctx` | -The [extension cookbook](cookbook/extension-cookbook.md) maps features to capabilities and indexes the step-by-step guides for [packages](cookbook/adding-a-package.md), [tools](cookbook/adding-a-tool.md), [LLM adapters](cookbook/adding-an-llm-adapter.md), and [settings cards](cookbook/adding-a-settings-card.md). The [Conversation subsystem](subsystems/conversation.md) owns Chat-node assembly. +The [extension cookbook](cookbook/extension-cookbook.md) maps features to capabilities and indexes the step-by-step guides for [packages](cookbook/adding-a-package.md), [tools](cookbook/adding-a-tool.md), [LLM adapters](cookbook/adding-an-llm-adapter.md), and [settings pages](cookbook/adding-a-settings-card.md). The [Conversation subsystem](subsystems/conversation.md) owns Chat-node assembly. diff --git a/docs/architecture.zh.md b/docs/architecture.zh.md index 084fa76a04..0a19671c6b 100644 --- a/docs/architecture.zh.md +++ b/docs/architecture.zh.md @@ -165,4 +165,4 @@ seam 正是替换一个提供方就能改变整个产品的原因。文件系统 | 在新后端存储会话 | 基于共享的句柄脚手架实现 `SessionPersistence`(`create`/`open`/`stat`/`list`/`export`) | | 将注册项限定到单个 agent | 使用该 agent 的 `agent.ctx` | -[扩展实操手册](cookbook/extension-cookbook.zh.md)将功能映射到能力,并索引[包](cookbook/adding-a-package.zh.md)、[工具](cookbook/adding-a-tool.zh.md)、[LLM(大语言模型)适配器](cookbook/adding-an-llm-adapter.zh.md)和[设置卡片](cookbook/adding-a-settings-card.zh.md)的分步指南。[Conversation 子系统](subsystems/conversation.zh.md)负责 Chat node 组装。 +[扩展实操手册](cookbook/extension-cookbook.zh.md)将功能映射到能力,并索引[包](cookbook/adding-a-package.zh.md)、[工具](cookbook/adding-a-tool.zh.md)、[LLM(大语言模型)适配器](cookbook/adding-an-llm-adapter.zh.md)和[设置页](cookbook/adding-a-settings-card.zh.md)的分步指南。[Conversation 子系统](subsystems/conversation.zh.md)负责 Chat node 组装。 diff --git a/docs/cookbook/adding-a-settings-card.i18n.yaml b/docs/cookbook/adding-a-settings-card.i18n.yaml index d04ea95824..1c493f63fe 100644 --- a/docs/cookbook/adding-a-settings-card.i18n.yaml +++ b/docs/cookbook/adding-a-settings-card.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/cookbook/adding-a-settings-card.md -adding-a-settings-card.md: cc8daa90bcc3a757cd41dbcaecb0eab662d1dba8 -adding-a-settings-card.zh.md: a0fce4075b9e29630c772bc52e07cd00d89d5bab +adding-a-settings-card.md: 72d7eaeb85d0d4f6981e814a8c335428c561b6bd +adding-a-settings-card.zh.md: d1254078085ba1de541ea70db566bcfb1bd50d1d diff --git a/docs/cookbook/adding-a-settings-card.md b/docs/cookbook/adding-a-settings-card.md index cc8daa90bc..72d7eaeb85 100644 --- a/docs/cookbook/adding-a-settings-card.md +++ b/docs/cookbook/adding-a-settings-card.md @@ -84,7 +84,7 @@ export function apply(ctx: ClientContext): void { A plugin with something to say about a bundle, a row, or an official plugin it does not own registers into `plugins.detail.actions` (a control at the head of the page), `plugins.detail.badge` (a tag beside the title), or `plugins.detail.section` (a section under the page's own content). Every entry is rendered with the page's `subject` — `{ kind: 'bundle', pkg }`, `{ kind: 'row', pkg, row }`, or `{ kind: 'item', id }` — and returns null for a subject it has nothing for: -```ts ignore-check +```tsx ignore-check ctx.slots.inject('plugins.detail.badge', () => ctx.slots.register({ name: 'plugins.detail.badge', id: 'acme-update', @@ -96,6 +96,8 @@ ctx.slots.inject('plugins.detail.badge', () => ctx.slots.register({ The browser half is served to the page by the [client module system](../../packages/client/modules), which scans the enabled Loader entries for packages declaring `dsh.client` and serves each one's built `./client` export — but it attaches a package's half to the Loader row whose specifier is the bare package name. A row mounted from a subpath export never carries a half, so a bundle that splits one package into several rows keeps its half on the root row, and every page it registers goes away when that row is switched off. A sub-plugin whose page must outlive the other rows ships as its own package. +The built `./client` file must be in the client module system's lazy-CJS factory format: one script that registers the package name and a `factory(require)` with the page's module loader, described in the [client module system's README](../../packages/client/modules/README.md). The `clientBundle` tsdown preset that emits it lives in `packages/client/tsdown.client.ts` rather than in a published package, so a package outside this repository reproduces that build itself. + ```jsonc { "exports": { diff --git a/docs/cookbook/adding-a-settings-card.zh.md b/docs/cookbook/adding-a-settings-card.zh.md index a0fce4075b..d125407808 100644 --- a/docs/cookbook/adding-a-settings-card.zh.md +++ b/docs/cookbook/adding-a-settings-card.zh.md @@ -84,7 +84,7 @@ export function apply(ctx: ClientContext): void { 对某个不属于自己的组合包、行或官方插件有话要说的插件,注册进 `plugins.detail.actions`(页头的控件)、`plugins.detail.badge`(标题旁的标签)或 `plugins.detail.section`(页面自身内容之下的区块)。每个条目都以页面的 `subject` 渲染——`{ kind: 'bundle', pkg }`、`{ kind: 'row', pkg, row }` 或 `{ kind: 'item', id }`——对无话可说的 subject 返回 null: -```ts ignore-check +```tsx ignore-check ctx.slots.inject('plugins.detail.badge', () => ctx.slots.register({ name: 'plugins.detail.badge', id: 'acme-update', @@ -96,6 +96,8 @@ ctx.slots.inject('plugins.detail.badge', () => ctx.slots.register({ 浏览器半侧由[客户端模块系统](../../packages/client/modules)送到页面:它扫描已启用的 Loader 条目,找出声明了 `dsh.client` 的包,送出每个包构建好的 `./client` 导出——但它只把一个包的半侧挂在说明符恰为裸包名的那一行上。从子路径导出挂载的行永远不带半侧,因此把一个包拆成多行的组合包,其半侧留在根行上,它注册的每个页面都随根行关闭而消失。需要在其他行关闭时仍保留页面的子插件,应作为独立的包发布。 +构建出的 `./client` 文件必须是客户端模块系统的 lazy-CJS factory 格式:一段脚本,向页面的模块加载器登记包名和一个 `factory(require)`,见[客户端模块系统的 README](../../packages/client/modules/README.zh.md)。生成它的 `clientBundle` tsdown 预设位于 `packages/client/tsdown.client.ts`,而不在任何已发布的包里,因此仓库之外的包要自己复刻这一步构建。 + ```jsonc { "exports": { diff --git a/packages/bundle/web-app/cordis.patch.yml b/packages/bundle/web-app/cordis.patch.yml index 8ade59ad22..8966c3d3e0 100644 --- a/packages/bundle/web-app/cordis.patch.yml +++ b/packages/bundle/web-app/cordis.patch.yml @@ -359,8 +359,9 @@ - id: ui-agent-preset name: '@deepseek-ai/dsh-client-ui-agent-preset' - # Plugin configuration: the host-plane sections a user owns, as expandable - # cards. A namespace this deployment does not expose renders nothing. + # The Built-in plugins settings section: the navigation entry and the tab + # row the inventory tab registers into. The official configuration pages + # are the companion rows below. - id: ui-settings-plugins name: '@deepseek-ai/dsh-client-ui-settings-plugins' diff --git a/packages/client/README.i18n.yaml b/packages/client/README.i18n.yaml index 2a01a6de69..89f30f6340 100644 --- a/packages/client/README.i18n.yaml +++ b/packages/client/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/README.md -README.md: 3a1a54f801090456a1038d0e3d59fe5223374f3c -README.zh.md: 231449ab574224a3e4fc5436c57e9ece5b0183b5 +README.md: 7b6c88ebbbfdf97f1ec191a2e8133c488ecb422d +README.zh.md: 11846c189afd85817d33e8266bb105b5f5418314 diff --git a/packages/client/README.md b/packages/client/README.md index 3a1a54f801..7b6c88ebbb 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -67,12 +67,16 @@ The kernel packages boot and serve the page; the UI feature packages present it. | [`ui-model-selection/`](ui-model-selection/README.md) | Provides model selection in conversation surfaces | — | | [`ui-permission-presets/`](ui-permission-presets/README.md) | Configures default permissions and switches the current session's access | — | | [`ui-plan/`](ui-plan/README.md) | Presents active plan-mode status and its exit control | — | -| [`ui-settings-plugins/`](ui-settings-plugins/README.md) | Owns the Plugins settings section, its tab extension point, and configurable host-plane plugin cards | — | +| [`ui-settings-plugins/`](ui-settings-plugins/README.md) | Owns the Built-in plugins settings section shell and its tab extension point | — | | [`ui-user-questions/`](ui-user-questions/README.md) | Presents interactive questions requested by the agent | — | | [`ui-agent-preset/`](ui-agent-preset/README.md) | Selects a session's agent preset and authors preset compositions | — | | [`ui-settings/`](ui-settings/README.md) | Hosts the settings interface and its extension areas | — | | [`ui-settings-general/`](ui-settings-general/README.md) | Provides the general settings section | — | | [`ui-settings-models/`](ui-settings-models/README.md) | Provides model-provider configuration and DeepSeek onboarding | — | +| [`ui-settings-shell/`](ui-settings-shell/README.md) | Provides the shell settings page on the Plugins page | — | +| [`ui-settings-agent-loop/`](ui-settings-agent-loop/README.md) | Provides the agent-loop settings page on the Plugins page | — | +| [`ui-settings-subagent/`](ui-settings-subagent/README.md) | Provides the Subagent settings page on the Plugins page | — | +| [`ui-settings-web-search/`](ui-settings-web-search/README.md) | Provides the web-search settings page on the Plugins page | — | | [`ui-plugin-manager/`](ui-plugin-manager/README.md) | Contributes the sidebar Plugins panel: install, enable, disable, retry, and compose installed packages | — | | [`ui-settings-plugin-inventory/`](ui-settings-plugin-inventory/README.md) | Contributes the read-only Host Loader inventory tab to Plugins settings | — | | [`ui-deliverables/`](ui-deliverables/README.md) | Produces the changed-files card with its comparison tabs, delivery cards, and clickable final-response file references | — | diff --git a/packages/client/README.zh.md b/packages/client/README.zh.md index 231449ab57..11846c189a 100644 --- a/packages/client/README.zh.md +++ b/packages/client/README.zh.md @@ -67,12 +67,16 @@ kind: "package-group" | [`ui-model-selection/`](ui-model-selection/README.zh.md) | 在对话界面中提供模型选择 | — | | [`ui-permission-presets/`](ui-permission-presets/README.zh.md) | 配置默认权限并切换当前会话的访问模式 | — | | [`ui-plan/`](ui-plan/README.zh.md) | 展示生效中的 plan mode 状态及其退出控件 | — | -| [`ui-settings-plugins/`](ui-settings-plugins/README.zh.md) | 负责「插件」设置分区、其标签页扩展点与可配置的宿主平面插件卡片 | — | +| [`ui-settings-plugins/`](ui-settings-plugins/README.zh.md) | 负责「内置插件」设置分区壳及其标签页扩展点 | — | | [`ui-user-questions/`](ui-user-questions/README.zh.md) | 展示 agent 请求的交互式问题 | — | | [`ui-agent-preset/`](ui-agent-preset/README.zh.md) | 选择会话的 agent 预设并编写预设组合 | — | | [`ui-settings/`](ui-settings/README.zh.md) | 承载设置界面及其扩展区域 | — | | [`ui-settings-general/`](ui-settings-general/README.zh.md) | 提供常规设置分区 | — | | [`ui-settings-models/`](ui-settings-models/README.zh.md) | 提供模型提供方配置与 DeepSeek 引导 | — | +| [`ui-settings-shell/`](ui-settings-shell/README.zh.md) | 在插件页提供终端设置页 | — | +| [`ui-settings-agent-loop/`](ui-settings-agent-loop/README.zh.md) | 在插件页提供 Agent 循环设置页 | — | +| [`ui-settings-subagent/`](ui-settings-subagent/README.zh.md) | 在插件页提供 Subagent 设置页 | — | +| [`ui-settings-web-search/`](ui-settings-web-search/README.zh.md) | 在插件页提供网页搜索设置页 | — | | [`ui-plugin-manager/`](ui-plugin-manager/README.zh.md) | 贡献侧栏的“插件”面板:安装、启用、停用、重试与组合已安装的包 | — | | [`ui-settings-plugin-inventory/`](ui-settings-plugin-inventory/README.zh.md) | 向「插件」设置贡献只读的 Host Loader 清单标签页 | — | | [`ui-deliverables/`](ui-deliverables/README.zh.md) | 生成改动文件卡片及其对比 tab、交付文件卡片与可点击的最终响应文件引用 | — | diff --git a/packages/client/ui-plugin-manager/README.i18n.yaml b/packages/client/ui-plugin-manager/README.i18n.yaml index 795636bb9f..7c58898be2 100644 --- a/packages/client/ui-plugin-manager/README.i18n.yaml +++ b/packages/client/ui-plugin-manager/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-plugin-manager/README.md -README.md: 4b6c6752f8dc71f7c00324cf56d236631810f752 -README.zh.md: f7a9ebe9efba83e6b9f5fb46b6422c2e099ead04 +README.md: a31960cab79b693f3d0f3b01a26d484b96ec404f +README.zh.md: 86f06fbeaaf0935f4d4cbdc639e95e6c75173dd6 diff --git a/packages/client/ui-plugin-manager/README.md b/packages/client/ui-plugin-manager/README.md index 4b6c6752f8..a31960cab7 100644 --- a/packages/client/ui-plugin-manager/README.md +++ b/packages/client/ui-plugin-manager/README.md @@ -45,7 +45,7 @@ A row's switch on the bundle's page calls `pluginManager.setPluginEnabled`, whic ### Configuration pages -A plugin that carries its own configuration renders it on this page rather than in Settings, through three slots the page declares: `plugins.item` (list) for an official plugin, listed in the Official group by its `label`; `plugins.bundle.config` (keyed by the bundle's package name) for a bundle's own configuration, shown on the bundle's page between its description and its rows; and `plugins.row.config` (keyed by `#`) for one row's configuration, which gives that row a **Configure** control opening the row's page. The page asks every entry for two views through its owner props: `view: 'summary'` for the one-liner under the title, `view: 'page'` for the form with its own save control. Only a save writes: the page draws the title, icon, and crumb, and the entry's form drops its staged edits when the page is left. The four host-plane pages the installation ships — the shell executor, the agent loop, subagent model selection, and the DeepSeek search provider — come from [ui-settings-plugins](../ui-settings-plugins/README.md), registered while the Host serves their namespaces. A bundle's browser half registers the same way: +A plugin that carries its own configuration renders it on this page rather than in Settings, through three slots the page declares: `plugins.item` (list) for an official plugin, listed in the Official group by its `label`; `plugins.bundle.config` (keyed by the bundle's package name) for a bundle's own configuration, shown on the bundle's page between its description and its rows; and `plugins.row.config` (keyed by `#`) for one row's configuration, which gives that row a **Configure** control opening the row's page. The page asks every entry for two views through its owner props: `view: 'summary'` for the one-liner under the title, `view: 'page'` for the form with its own save control. Only a save writes: the page draws the title, icon, and crumb, and the entry's form drops its staged edits when the page is left. The four host-plane pages the installation ships — the shell executor, the agent loop, Subagent, and the DeepSeek search provider — come from one companion package each, [ui-settings-shell](../ui-settings-shell/README.md), [ui-settings-agent-loop](../ui-settings-agent-loop/README.md), [ui-settings-subagent](../ui-settings-subagent/README.md), and [ui-settings-web-search](../ui-settings-web-search/README.md), registered while the Host serves their namespaces. A bundle's browser half registers the same way: ```tsx ignore-check ctx.slots.inject('plugins.row.config', () => ctx.slots.register({ @@ -105,7 +105,7 @@ These pages cover the sidebar, the Remote calls, and the Host-side manager. - [ui-sidebar](../ui-sidebar/README.md) — the panel list the Plugins entry registers into; [ui-layout](../ui-layout/README.md) — the main slot the page occupies. - [api-remotes](../../api/remotes/README.md) — the Remote BFF surface behind `pluginManager.*` and `pluginInventory.*`. - [plugin-manager](../../boot/plugin-manager/README.md) — the Host-side manager this page drives. -- [ui-settings-plugins](../ui-settings-plugins/README.md) — the official configuration pages that register into this page's slots. +- [ui-settings-shell](../ui-settings-shell/README.md), [ui-settings-agent-loop](../ui-settings-agent-loop/README.md), [ui-settings-subagent](../ui-settings-subagent/README.md), [ui-settings-web-search](../ui-settings-web-search/README.md) — the official configuration pages that register into this page's `plugins.item` slot. ----- diff --git a/packages/client/ui-plugin-manager/README.zh.md b/packages/client/ui-plugin-manager/README.zh.md index f7a9ebe9ef..86f06fbeaa 100644 --- a/packages/client/ui-plugin-manager/README.zh.md +++ b/packages/client/ui-plugin-manager/README.zh.md @@ -45,7 +45,7 @@ Agent Teams、Agent Teams Web UI 和 Auto Authorization Review 三个包使用 ### 配置页 -自带配置的插件把配置渲染在本页而不是设置里,通过本页声明的三个 slot:`plugins.item`(list)用于官方插件,按其 `label` 列在官方分组里;`plugins.bundle.config`(以组合包的包名为键)用于组合包自己的配置,显示在组合包页面的描述与行之间;`plugins.row.config`(以 `<包名>#<行 id>` 为键)用于某一行的配置,这一行由此多出一个**配置**控件,打开该行自己的页面。页面通过 owner props 向每个条目索取两种视图:`view: 'summary'` 是标题下的一句话简介,`view: 'page'` 是带自己保存控件的表单。只有保存才写入:页面负责画标题、图标与面包屑,条目的表单在离开页面时丢弃暂存的修改。安装随附的四个宿主平面配置页——shell 执行器、agent loop、subagent 模型选择、DeepSeek 搜索提供方——来自 [ui-settings-plugins](../ui-settings-plugins/README.zh.md),在 Host 服务其命名空间期间注册。组合包的浏览器半侧用同样的方式注册: +自带配置的插件把配置渲染在本页而不是设置里,通过本页声明的三个 slot:`plugins.item`(list)用于官方插件,按其 `label` 列在官方分组里;`plugins.bundle.config`(以组合包的包名为键)用于组合包自己的配置,显示在组合包页面的描述与行之间;`plugins.row.config`(以 `<包名>#<行 id>` 为键)用于某一行的配置,这一行由此多出一个**配置**控件,打开该行自己的页面。页面通过 owner props 向每个条目索取两种视图:`view: 'summary'` 是标题下的一句话简介,`view: 'page'` 是带自己保存控件的表单。只有保存才写入:页面负责画标题、图标与面包屑,条目的表单在离开页面时丢弃暂存的修改。安装随附的四个宿主平面配置页——shell 执行器、agent loop、Subagent、DeepSeek 搜索提供方——各来自一个伴生包:[ui-settings-shell](../ui-settings-shell/README.zh.md)、[ui-settings-agent-loop](../ui-settings-agent-loop/README.zh.md)、[ui-settings-subagent](../ui-settings-subagent/README.zh.md) 与 [ui-settings-web-search](../ui-settings-web-search/README.zh.md),在 Host 服务其命名空间期间注册。组合包的浏览器半侧用同样的方式注册: ```tsx ignore-check ctx.slots.inject('plugins.row.config', () => ctx.slots.register({ @@ -105,7 +105,7 @@ ctx.slots.inject('plugins.detail.section', () => ctx.slots.register({ - [ui-sidebar](../ui-sidebar/README.zh.md)——插件入口注册进的面板列表;[ui-layout](../ui-layout/README.zh.md)——页面占用的主 slot。 - [api-remotes](../../api/remotes/README.zh.md)——`pluginManager.*` 与 `pluginInventory.*` 背后的 Remote BFF 面。 - [plugin-manager](../../boot/plugin-manager/README.zh.md)——本页驱动的 Host 侧管理器。 -- [ui-settings-plugins](../ui-settings-plugins/README.zh.md)——注册进本页 slot 的官方配置页。 +- [ui-settings-shell](../ui-settings-shell/README.zh.md)、[ui-settings-agent-loop](../ui-settings-agent-loop/README.zh.md)、[ui-settings-subagent](../ui-settings-subagent/README.zh.md)、[ui-settings-web-search](../ui-settings-web-search/README.zh.md)——注册进本页 `plugins.item` slot 的官方配置页。 ----- diff --git a/packages/client/ui-primitives/tests/settings-form-model.client.spec.ts b/packages/client/ui-primitives/tests/settings-form-model.client.spec.ts index 66fb0b5ad8..a2a0ef86ca 100644 --- a/packages/client/ui-primitives/tests/settings-form-model.client.spec.ts +++ b/packages/client/ui-primitives/tests/settings-form-model.client.spec.ts @@ -270,7 +270,7 @@ describe('SettingsFormModel', () => { expect(store.getSnapshot()).toBe('2000') }) - it('refuses to address a field the card never declared', () => { + it('refuses to address a field the form never declared', () => { const { subject } = form() expect(() => subject.field('nope')).toThrow('settings form has no field nope') diff --git a/packages/client/ui-settings-subagent/package.json b/packages/client/ui-settings-subagent/package.json index 49ccf5295f..1f5858183d 100644 --- a/packages/client/ui-settings-subagent/package.json +++ b/packages/client/ui-settings-subagent/package.json @@ -30,7 +30,8 @@ "inject": [ "@deepseek-ai/dsh-client-locale", "@deepseek-ai/dsh-client-ui-settings", - "@deepseek-ai/dsh-client-ui-plugin-manager" + "@deepseek-ai/dsh-client-ui-plugin-manager", + "@deepseek-ai/dsh-api-remotes" ], "platform": "web" } diff --git a/packages/client/ui-settings-web-search/package.json b/packages/client/ui-settings-web-search/package.json index 4959b285bc..becf2a405a 100644 --- a/packages/client/ui-settings-web-search/package.json +++ b/packages/client/ui-settings-web-search/package.json @@ -30,7 +30,8 @@ "inject": [ "@deepseek-ai/dsh-client-locale", "@deepseek-ai/dsh-client-ui-settings", - "@deepseek-ai/dsh-client-ui-plugin-manager" + "@deepseek-ai/dsh-client-ui-plugin-manager", + "@deepseek-ai/dsh-api-remotes" ], "platform": "web" } diff --git a/packages/client/ui-settings/README.i18n.yaml b/packages/client/ui-settings/README.i18n.yaml index f325c7e238..861b4012de 100644 --- a/packages/client/ui-settings/README.i18n.yaml +++ b/packages/client/ui-settings/README.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/client/ui-settings/README.md -README.md: 760b4a4567481b561ce319926149815119018177 -README.zh.md: f8e2128d734b650b7cd7a10c180f90ea01b77ab5 +README.md: d19de7bea23b1d752b977aea06d868e029fc3331 +README.zh.md: 5e30e94f7436dc93c3d6f06a58ed771aa314bff7 diff --git a/packages/client/ui-settings/README.md b/packages/client/ui-settings/README.md index 760b4a4567..d19de7bea2 100644 --- a/packages/client/ui-settings/README.md +++ b/packages/client/ui-settings/README.md @@ -31,6 +31,10 @@ Feature plugins use this package to store and edit their preferences without re- A feature calls `ctx.settingsScope.bind(spec)` with a per-namespace spec and gets a scope derived from the shared document mirror. The scope snapshot carries the resolved section, composition `base`, raw `user`, revision, writability, and host/memory mode; a field is overridden when it is present in `user`, even when its value equals `base`, and `unset` clears that override. Writes go through the scope: `set` and `unset` submit one operation, while `mutate` submits several ordered operations atomically. Each write is fenced by the namespace revision as `expectedRevision`, so a concurrent write from another surface is refused instead of silently overwritten. A staged editor can supply the revision where its draft began as a fixed fence; otherwise the scope uses the latest queued or mirrored revision. +### Following served namespaces + +A page that edits a namespace another plugin owns registers through `ctx.settingsScope.whileServed(namespaces, register)`: `register` runs once any listed namespace is in the shared mirror and returns the registration's disposer, which runs when none of them is served any more or when the disposer `whileServed` returns runs. The caller owns that returned disposer and wraps it in `ctx.effect`; unlike `bind`, the service registers nothing on the caller's context. A deployment that never composed the owner therefore shows no trace of the page, and a namespace the Host stops serving withdraws it. The four official pages on the Plugins page ride it, one companion package each. + ### Filling the settings slots A settings surface registers into the slot types this package declares. The shell (`sidebar.settings` occupant, navigation, chrome) lives in ui-settings-general; feature pages register `settings.section` contributions; the Plugins section hosts `settings.plugins.tab` pages; onboarding steps register `settings.onboarding`. Cross-namespace surfaces (schema introspection, the served-namespace directory, `hasDocument`) read the same mirror through `ctx.settingsScope.describe()`. @@ -55,7 +59,7 @@ The plugin injects `remote` with its `settings` namespace, resolves Host persist ### Scope derivation -`ctx.settingsScope.bind(spec)` returns a per-namespace scope derived from the mirror on the caller's context: the scope's disposer belongs to the calling fiber, binding adds no wire read, and a row's activation never blocks on the settings transport. Writes stay per-scope: `set` and `unset` are single-operation forms of `mutate`, which copies and queues several ordered field operations behind one namespace revision as `expectedRevision`. A committed mutation folds its answer in, a rejected or failed latest mutation triggers one recovery read, and a superseded one leaves recovery to its successor. The cold-boot read count is pinned by `../../../apps/web/tests/startup-rpc-budget.e2e.ts`; a new direct `settings.describe` caller in client code is a regression against it. +`ctx.settingsScope.bind(spec)` returns a per-namespace scope derived from the mirror on the caller's context: the scope's disposer belongs to the calling fiber, binding adds no wire read, and a row's activation never blocks on the settings transport. Writes stay per-scope: `set` and `unset` are single-operation forms of `mutate`, which copies and queues several ordered field operations behind one namespace revision as `expectedRevision`. A committed mutation folds its answer in, a rejected or failed latest mutation triggers one recovery read, and a superseded one leaves recovery to its successor. The cold-boot read count is pinned by `../../../apps/web/tests/startup-rpc-budget.e2e.ts`; a new direct `settings.describe` caller in client code is a regression against it. `whileServed` subscribes to the same mirror and re-evaluates its served set on every snapshot, so a registration toggles with the describe view and needs no wire read beyond the mirror's `ensure`. ### Schema service @@ -71,7 +75,8 @@ The plugin injects `remote` with its `settings` namespace, resolves Host persist These pages cover the settings surface family and the durable seam behind it. - [ui-settings-general](../ui-settings-general/README.md) — the settings shell: trigger chrome, navigation, General section, onboarding projection. -- [ui-settings-plugins](../ui-settings-plugins/README.md) — the Plugins section and its configurable host-plane cards. +- [ui-settings-plugins](../ui-settings-plugins/README.md) — the Built-in plugins section shell around the inventory tab. +- [ui-settings-shell](../ui-settings-shell/README.md), [ui-settings-agent-loop](../ui-settings-agent-loop/README.md), [ui-settings-subagent](../ui-settings-subagent/README.md), [ui-settings-web-search](../ui-settings-web-search/README.md) — the official configuration pages on the Plugins page, each following its namespaces through `whileServed`. - [ui-settings-models](../ui-settings-models/README.md) — the Models page and DeepSeek onboarding over this base. - [settings](../../settings/README.md) — the durable user-settings seam and its file provider. - [ui-sidebar](../ui-sidebar/README.md) — the sidebar shell whose bottom seat hosts the settings trigger. diff --git a/packages/client/ui-settings/README.zh.md b/packages/client/ui-settings/README.zh.md index f8e2128d73..5e30e94f74 100644 --- a/packages/client/ui-settings/README.zh.md +++ b/packages/client/ui-settings/README.zh.md @@ -31,6 +31,10 @@ kind: "package-reference" 功能调用 `ctx.settingsScope.bind(spec)` 并传入按命名空间的 spec,得到一个由共享文档镜像派生的 scope。scope 快照携带解析后的分区、组合 `base`、原始 `user`、revision、可写性以及 host/内存模式;字段只要出现在 `user` 中即视为覆盖,即使其值与 `base` 相等,`unset` 会清除该覆盖。写入经 scope 进行:`set` 与 `unset` 提交一个操作,`mutate` 则原子提交多个有序操作。每次写入都以命名空间 revision 作为 `expectedRevision` 围栏,因此来自另一界面的并发写入会被拒绝,而不是被静默覆盖。暂存编辑器可以把开始草拟时读取的 revision 作为固定围栏传入;否则 scope 使用最新排队或镜像 revision。 +### 跟随被服务的命名空间 + +编辑另一个插件所拥有命名空间的页面通过 `ctx.settingsScope.whileServed(namespaces, register)` 注册:只要所列命名空间中的任一个进入共享镜像,`register` 就运行一次并返回该注册的 disposer;当它们全都不再被服务,或 `whileServed` 返回的 disposer 被调用时,这个 disposer 就运行。调用方持有返回的 disposer 并把它包进 `ctx.effect`;与 `bind` 不同,服务不会在调用方的 context 上注册任何东西。因此从未组合过所有者的部署看不到该页面的任何痕迹,Host 停止服务的命名空间会撤下它。插件页上的四个官方页面各自通过一个伴生包走这条路径。 + ### 填充设置 slot 设置界面会注册进本包声明的 slot 类型。外壳(`sidebar.settings` 占位方、导航、界面框架)位于 ui-settings-general;功能页面注册 `settings.section` 贡献;「插件」分区承载 `settings.plugins.tab` 页面;首次使用引导步骤注册 `settings.onboarding`。跨命名空间的表面(schema 内省、已服务命名空间目录、`hasDocument`)通过 `ctx.settingsScope.describe()` 读同一面镜像。 @@ -55,7 +59,7 @@ kind: "package-reference" ### Scope 派生 -`ctx.settingsScope.bind(spec)` 在调用方的 context 上返回一个由镜像派生的按命名空间 scope:scope 的 disposer 归调用方 fiber 所有,绑定不新增任何线路读取,某一行的激活绝不会阻塞在设置传输层上。写入仍归各 scope:`set` 与 `unset` 是 `mutate` 的单操作形式,后者会复制操作列表,并把多个有序字段操作排在同一个作为 `expectedRevision` 的命名空间 revision 之后。提交成功的 mutation 把应答折回镜像,被拒绝或失败的最新 mutation 触发一次恢复读取,被取代的 mutation 把恢复留给后继者。冷启动读取次数由 `../../../apps/web/tests/startup-rpc-budget.e2e.ts` 钉住;客户端代码中新增直连 `settings.describe` 调用即是对它的回归。 +`ctx.settingsScope.bind(spec)` 在调用方的 context 上返回一个由镜像派生的按命名空间 scope:scope 的 disposer 归调用方 fiber 所有,绑定不新增任何线路读取,某一行的激活绝不会阻塞在设置传输层上。写入仍归各 scope:`set` 与 `unset` 是 `mutate` 的单操作形式,后者会复制操作列表,并把多个有序字段操作排在同一个作为 `expectedRevision` 的命名空间 revision 之后。提交成功的 mutation 把应答折回镜像,被拒绝或失败的最新 mutation 触发一次恢复读取,被取代的 mutation 把恢复留给后继者。冷启动读取次数由 `../../../apps/web/tests/startup-rpc-budget.e2e.ts` 钉住;客户端代码中新增直连 `settings.describe` 调用即是对它的回归。`whileServed` 订阅同一面镜像,在每次快照变化时重新判断被服务集合,因此注册随 describe 视图开关,除镜像的 `ensure` 外不需要任何线路读取。 ### Schema 服务 @@ -71,7 +75,8 @@ kind: "package-reference" 以下页面覆盖设置界面家族及其背后的持久化 seam。 - [ui-settings-general](../ui-settings-general/README.zh.md)——设置外壳:触发控件、导航、「通用」分区、引导投影。 -- [ui-settings-plugins](../ui-settings-plugins/README.zh.md)——「插件」分区及其可配置宿主平面卡片。 +- [ui-settings-plugins](../ui-settings-plugins/README.zh.md)——围绕清单标签页的「内置插件」分区壳。 +- [ui-settings-shell](../ui-settings-shell/README.zh.md)、[ui-settings-agent-loop](../ui-settings-agent-loop/README.zh.md)、[ui-settings-subagent](../ui-settings-subagent/README.zh.md)、[ui-settings-web-search](../ui-settings-web-search/README.zh.md)——插件页上的官方配置页,各自通过 `whileServed` 跟随其命名空间。 - [ui-settings-models](../ui-settings-models/README.zh.md)——建立在本底座之上的 Models 页面与 DeepSeek 引导。 - [settings](../../settings/README.zh.md)——持久化用户设置 seam 及其文件提供方。 - [ui-sidebar](../ui-sidebar/README.zh.md)——底部席位承载设置触发控件的侧边栏外壳。 diff --git a/packages/client/ui-settings/src/client/settings-scope.ts b/packages/client/ui-settings/src/client/settings-scope.ts index 2dec36af4e..f9fa3af55d 100644 --- a/packages/client/ui-settings/src/client/settings-scope.ts +++ b/packages/client/ui-settings/src/client/settings-scope.ts @@ -303,7 +303,9 @@ export class SettingsScopeBinder extends Service { * disposer runs when none is or when the returned disposer runs. A plugin * whose page edits a namespace another plugin owns registers the page * through this, so a deployment that never composed the owner shows no - * trace of the page. + * trace of the page. The caller owns the returned disposer and wraps it in + * `ctx.effect`; unlike {@link bind}, nothing is registered on the caller's + * context here. * @param namespaces - the settings namespaces the registration follows. * @param register - registers the contribution; returns its disposer. * @returns the disposer ending the watch and any live registration. diff --git a/website/docs.ts b/website/docs.ts index e366926c9c..5279241051 100644 --- a/website/docs.ts +++ b/website/docs.ts @@ -440,7 +440,7 @@ const reference = [ ['adding-a-package.md', '新增 Package', 'Adding a package'], ['adding-a-tool.md', '新增 Tool', 'Adding a tool'], ['adding-an-llm-adapter.md', '新增 LLM Adapter', 'Adding an LLM adapter'], - ['adding-a-settings-card.md', '新增设置卡片', 'Adding a settings card'], + ['adding-a-settings-card.md', '新增设置页', 'Adding a settings page'], ['extension-cookbook.md', '扩展模式', 'Extension patterns'], ] as const).map(([file, rootLabel, enLabel], order): PairedPage => ({ source: `docs/cookbook/${file}`,