Revert "feat(plugin-manager): let a bundle list its rows without row switches"
This reverts commit 089291591cbf9a9e08dbe99d96aa378a5f1ccac8.
This commit is contained in:
1 parent
6085a56eb6
commit
738178f136
20 files changed
+39
-68
No files matched your search
@@ -56,9 +56,9 @@ This matrix shows which packages dispatch each harness-owned event and which pac
|
||||
| `llm/adapters-updated` | `emit` | [`packages/llm/llm/src/types.ts:23`](../packages/llm/llm/src/types.ts) | [`llm`](../packages/llm/llm) (`events.dispatch`) | [`acp`](../packages/acp/acp), [`llm`](../packages/llm/llm), `remotes` |
|
||||
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:75`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`llm-replay`](../packages/test-support/llm-replay), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), [`session-title`](../packages/session/session-title) |
|
||||
| `permission-presets/catalog-changed` | `emit` | [`packages/interaction/permission-presets/src/types.ts:48`](../packages/interaction/permission-presets/src/types.ts) | [`permission-presets`](../packages/interaction/permission-presets) (`events.dispatch`) | `remotes` |
|
||||
| `plugin-manager/changed` | `emit` | [`packages/boot/plugin-manager/src/types.ts:245`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/install-log` | `emit` | [`packages/boot/plugin-manager/src/types.ts:251`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/install-state` | `emit` | [`packages/boot/plugin-manager/src/types.ts:258`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/changed` | `emit` | [`packages/boot/plugin-manager/src/types.ts:243`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/install-log` | `emit` | [`packages/boot/plugin-manager/src/types.ts:249`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/install-state` | `emit` | [`packages/boot/plugin-manager/src/types.ts:256`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `schedule/changed` | `emit` | [`packages/schedule/schedule/src/types.ts:471`](../packages/schedule/schedule/src/types.ts) | [`schedule`](../packages/schedule/schedule) (`emit`) | `remotes` |
|
||||
| `session-telemetry/record` | `waterfall` | [`packages/session/session-telemetry/src/index.ts:43`](../packages/session/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/session/session-telemetry) (`waterfall`) | - |
|
||||
| `session/created` | `emit` | [`packages/core/session/src/index.ts:55`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`compaction`](../packages/compaction/compaction), [`goal`](../packages/goal/goal), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`schedule`](../packages/schedule/schedule), `server`, [`session`](../packages/core/session), `session-controller`, [`session-log-deepseek`](../packages/session/session-log-deepseek), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-title`](../packages/session/session-title), [`time-context`](../packages/context/time-context), [`tool-todo`](../packages/todo/tool-todo), [`tool-workflow`](../packages/workflow/tool-workflow), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
|
||||
|
||||
@@ -58,9 +58,9 @@
|
||||
| `llm/adapters-updated` | `emit` | [`packages/llm/llm/src/types.ts:23`](../packages/llm/llm/src/types.ts) | [`llm`](../packages/llm/llm) (`events.dispatch`) | [`acp`](../packages/acp/acp), [`llm`](../packages/llm/llm), `remotes` |
|
||||
| `llm/stream` | `waterfall` | [`packages/llm/llm/src/index.ts:75`](../packages/llm/llm/src/index.ts) | [`llm`](../packages/llm/llm) (`waterfall`) | [`agent-loop`](../packages/core/agent-loop), [`llm`](../packages/llm/llm), [`llm-replay`](../packages/test-support/llm-replay), [`session-checkpoint-policy`](../packages/session/session-checkpoint-policy), [`session-title`](../packages/session/session-title) |
|
||||
| `permission-presets/catalog-changed` | `emit` | [`packages/interaction/permission-presets/src/types.ts:48`](../packages/interaction/permission-presets/src/types.ts) | [`permission-presets`](../packages/interaction/permission-presets) (`events.dispatch`) | `remotes` |
|
||||
| `plugin-manager/changed` | `emit` | [`packages/boot/plugin-manager/src/types.ts:245`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/install-log` | `emit` | [`packages/boot/plugin-manager/src/types.ts:251`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/install-state` | `emit` | [`packages/boot/plugin-manager/src/types.ts:258`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/changed` | `emit` | [`packages/boot/plugin-manager/src/types.ts:243`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/install-log` | `emit` | [`packages/boot/plugin-manager/src/types.ts:249`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `plugin-manager/install-state` | `emit` | [`packages/boot/plugin-manager/src/types.ts:256`](../packages/boot/plugin-manager/src/types.ts) | [`plugin-manager`](../packages/boot/plugin-manager) (`emit`) | `remotes` |
|
||||
| `schedule/changed` | `emit` | [`packages/schedule/schedule/src/types.ts:471`](../packages/schedule/schedule/src/types.ts) | [`schedule`](../packages/schedule/schedule) (`emit`) | `remotes` |
|
||||
| `session-telemetry/record` | `waterfall` | [`packages/session/session-telemetry/src/index.ts:43`](../packages/session/session-telemetry/src/index.ts) | [`session-telemetry`](../packages/session/session-telemetry) (`waterfall`) | - |
|
||||
| `session/created` | `emit` | [`packages/core/session/src/index.ts:55`](../packages/core/session/src/index.ts) | [`session`](../packages/core/session) (`events.dispatch`) | [`compaction`](../packages/compaction/compaction), [`goal`](../packages/goal/goal), [`hook-protocol`](../packages/hooks/hook-protocol), [`llm-retry`](../packages/llm/llm-retry), [`permission-presets`](../packages/interaction/permission-presets), [`plan-mode`](../packages/plan/plan-mode), [`schedule`](../packages/schedule/schedule), `server`, [`session`](../packages/core/session), `session-controller`, [`session-log-deepseek`](../packages/session/session-log-deepseek), [`session-projection`](../packages/session/session-projection), [`session-projection-cache`](../packages/session/session-projection-cache), [`session-telemetry`](../packages/session/session-telemetry), [`session-title`](../packages/session/session-title), [`time-context`](../packages/context/time-context), [`tool-todo`](../packages/todo/tool-todo), [`tool-workflow`](../packages/workflow/tool-workflow), [`tools`](../packages/core/tools), [`user-approval`](../packages/interaction/user-approval) |
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
en: 87679c51aa3693f5
|
||||
zh: 11684a48a5806afb
|
||||
/deepseek-ai-dsh-plugin-manager/use-this-package:
|
||||
en: 6d0181eebb089d9d
|
||||
zh: 73b0c782bd017dd4
|
||||
en: b8d7061ee289b2a0
|
||||
zh: 49aba7df4761fb23
|
||||
/deepseek-ai-dsh-plugin-manager/use-this-package/version-compatibility-and-exemptions:
|
||||
en: 920c0acb3b9feaf3
|
||||
zh: ba6e832a256880a7
|
||||
|
||||
@@ -41,7 +41,7 @@ A plugin toggle updates only `disabled` in the last matching override in the pro
|
||||
|
||||
A selected bundle that cannot load remains in `listBundles` with an `error`; `enabled` records the saved selection, not successful loading. The plugin page shows the error and allows deselection. A broken bundle cannot be enabled. Management bundles remain protected if their files become unreadable.
|
||||
|
||||
`listBundles` includes optional display `meta` for each bundle and its declared plugin rows, including disabled bundles. The Client selects a language from these values. The separate `description` field is the bundle's raw `package.json.description`; a metadata diagnostic does not block management. The `plugin_manager` tool omits UI display metadata from list results. `rowSwitches` reports the bundle's `dsh.bundle.rowSwitches`, true unless the bundle sets it to false.
|
||||
`listBundles` includes optional display `meta` for each bundle and its declared plugin rows, including disabled bundles. The Client selects a language from these values. The separate `description` field is the bundle's raw `package.json.description`; a metadata diagnostic does not block management. The `plugin_manager` tool omits UI display metadata from list results.
|
||||
|
||||
`inspect(spec, options)` reads what a spec names before anything installs: a registry name is asked of the registry through `pnpm view`, run in the profile directory so the same proxy and authentication settings apply as to the install; an absolute path has its `package.json` read; a git address or tarball answers only its form and the `host` it is fetched from. The answer carries the name, version, description, whether the package declares a bundle, and the `registry` that answered, or a `problem`: `invalid-spec`, `already-installed`, `not-found`, `not-a-package`, `not-a-bundle`, `network`, or `unknown`, with the `registries` asked. A caller's `signal` or `inspectTimeoutMs` ends the lookup.
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ kind: "package-reference"
|
||||
|
||||
已选择但无法加载的组合包仍会出现在 `listBundles` 中,并携带 `error`;`enabled` 表示保存的选择,不代表加载成功。插件页面显示错误并允许取消选择。损坏的组合包无法启用。管理组合包的文件变得不可读后仍受保护。
|
||||
|
||||
`listBundles` 为各组合包及其声明的插件行提供可选的展示 `meta`,包括已禁用的组合包。Client 从这些值中选择语言。单独的 `description` 字段是该组合包原始的 `package.json.description`;元信息诊断不会阻止管理操作。`plugin_manager` 工具的列表结果不包含 UI 展示元信息。`rowSwitches` 报告组合包的 `dsh.bundle.rowSwitches`,除非组合包将其设为 false,否则为 true。
|
||||
`listBundles` 为各组合包及其声明的插件行提供可选的展示 `meta`,包括已禁用的组合包。Client 从这些值中选择语言。单独的 `description` 字段是该组合包原始的 `package.json.description`;元信息诊断不会阻止管理操作。`plugin_manager` 工具的列表结果不包含 UI 展示元信息。
|
||||
|
||||
`inspect(spec, options)` 在任何东西安装之前读出 spec 指向什么:注册表包名通过 `pnpm view` 询问注册表,在 profile 目录中运行,因而与安装使用同样的代理与认证设置;绝对路径读取其 `package.json`;git 地址或 tarball 只答复自己的形式和它被拉取的 `host`。答复携带名称、版本、描述、该包是否声明组合包,以及作答的 `registry`,否则给出 `problem`:`invalid-spec`、`already-installed`、`not-found`、`not-a-package`、`not-a-bundle`、`network` 或 `unknown`,并附上问过的 `registries`。调用方的 `signal` 或 `inspectTimeoutMs` 会结束查询。
|
||||
|
||||
|
||||
@@ -295,8 +295,7 @@ export class PluginManager extends TypertRemoteService {
|
||||
const info = bundleManifest(name, this.profile.dir, this.profile.installAnchor)
|
||||
if (info === undefined) {
|
||||
if (enabled) bundles.push({ name, enabled, installed, optional, removable: removable && readOnlyReason === undefined,
|
||||
...(readOnlyReason === undefined ? {} : { readOnlyReason }), error: { code: 'not-bundle' },
|
||||
rows: [], rowSwitches: true, overrides: [] })
|
||||
...(readOnlyReason === undefined ? {} : { readOnlyReason }), error: { code: 'not-bundle' }, rows: [], overrides: [] })
|
||||
continue
|
||||
}
|
||||
const compatibility = evaluatePluginCompatibility(info, exemptions)
|
||||
@@ -312,8 +311,7 @@ export class PluginManager extends TypertRemoteService {
|
||||
} catch (error) {
|
||||
if (enabled || installed) {
|
||||
bundles.push({ name, enabled, installed, optional, removable: removable && readOnlyReason === undefined,
|
||||
...(readOnlyReason === undefined ? {} : { readOnlyReason }), error: managementError(error),
|
||||
rows: [], rowSwitches: true, overrides: [] })
|
||||
...(readOnlyReason === undefined ? {} : { readOnlyReason }), error: managementError(error), rows: [], overrides: [] })
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -627,11 +625,11 @@ export class PluginManager extends TypertRemoteService {
|
||||
}, { stage: 'remove', target: name }, 'remove')
|
||||
}
|
||||
|
||||
/** The rows a bundle's patch inserts, whether each gets a switch, and the existing rows it changes; an unreadable patch throws. */
|
||||
private declaredRows(name: string, info: ProfileManifest): Pick<BundleInfo, 'rows' | 'rowSwitches' | 'overrides'> {
|
||||
/** The rows a bundle's patch inserts and the existing rows it changes; an unreadable patch throws. */
|
||||
private declaredRows(name: string, info: ProfileManifest): Pick<BundleInfo, 'rows' | 'overrides'> {
|
||||
const bundle = info.dsh?.bundle
|
||||
/* v8 ignore next -- bundleManifest answers only manifests that declare a patch */
|
||||
if (bundle === undefined) return { rows: [], rowSwitches: true, overrides: [] }
|
||||
if (bundle === undefined) return { rows: [], overrides: [] }
|
||||
const dir = resolveBundleDir('dsh', name, this.profile.installAnchor, this.profile.dir)
|
||||
const patches: PatchOptions[] = bundlePatchPaths(dir, bundle).flatMap(file => loadOverlayPatches('dsh', file))
|
||||
// One entry per row id: the Loader keeps a single entry for an id, whichever layer declared it last.
|
||||
@@ -656,7 +654,7 @@ export class PluginManager extends TypertRemoteService {
|
||||
const declared = new Set(rows.map(row => row.rowId))
|
||||
const overrides = [...new Set(patches.flatMap(item =>
|
||||
item.insert === undefined && typeof item.id === 'string' && !declared.has(item.id) ? [item.id] : []))]
|
||||
return { rows, rowSwitches: bundle.rowSwitches !== false, overrides }
|
||||
return { rows, overrides }
|
||||
}
|
||||
|
||||
/** Run one pnpm command in the profile, streaming its output as install-log chunks. */
|
||||
|
||||
@@ -65,8 +65,6 @@ export interface BundleInfo {
|
||||
error?: ManagementError
|
||||
/** The rows the bundle's patch inserts, in declaration order; empty when the patch cannot be read. */
|
||||
rows: BundleRowInfo[]
|
||||
/** Whether the plugin manager offers a switch for each inserted row: false when the bundle's `dsh.bundle.rowSwitches` is false. */
|
||||
rowSwitches: boolean
|
||||
/** Ids of rows the bundle's patch changes without declaring them: the built-in rows it configures or disables. */
|
||||
overrides: string[]
|
||||
}
|
||||
|
||||
@@ -292,21 +292,21 @@ it('lists bundle versions and current-profile plugin targets', async () => {
|
||||
{
|
||||
name: 'core', version: '1.0.0', enabled: true, installed: false, optional: false, removable: false, readOnlyReason: 'management-required',
|
||||
meta: { title: 'core' },
|
||||
rows: [{ rowId: 'manager', moduleName: 'cordis:manager', entryId: 'include:manager' }], rowSwitches: true, overrides: [],
|
||||
rows: [{ rowId: 'manager', moduleName: 'cordis:manager', entryId: 'include:manager' }], overrides: [],
|
||||
},
|
||||
{
|
||||
name: 'extra', version: '1.0.0', enabled: true, installed: true, optional: false, removable: true,
|
||||
meta: { title: 'extra' },
|
||||
rows: [{ rowId: 'managed', moduleName: pathToFileURL(join(dir, 'node_modules', 'extra', 'plugin.mjs')).href, entryId: 'include:managed' }], rowSwitches: true, overrides: [],
|
||||
rows: [{ rowId: 'managed', moduleName: pathToFileURL(join(dir, 'node_modules', 'extra', 'plugin.mjs')).href, entryId: 'include:managed' }], overrides: [],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('describes a bundle by its manifest and patch: one-liner, rows without a live entry, row switches, and the built-in rows it changes', async () => {
|
||||
it('describes a bundle by its manifest and patch: one-liner, rows without a live entry, and the built-in rows it changes', async () => {
|
||||
const { manager, dir, bundle } = await fixture()
|
||||
bundle('described', [{ id: 'described-row', name: './plugin.mjs' }])
|
||||
writeFileSync(join(dir, 'node_modules', 'described', 'package.json'), JSON.stringify({
|
||||
name: 'described', version: '2.0.0', description: 'Describes itself.', dsh: { bundle: { patch: './cordis.patch.yml', rowSwitches: false } },
|
||||
name: 'described', version: '2.0.0', description: 'Describes itself.', dsh: { bundle: { patch: './cordis.patch.yml' } },
|
||||
}))
|
||||
// An anonymous row is not addressable and is left out of the rows.
|
||||
writeFileSync(join(dir, 'node_modules', 'described', 'cordis.patch.yml'), JSON.stringify([
|
||||
@@ -319,7 +319,7 @@ it('describes a bundle by its manifest and patch: one-liner, rows without a live
|
||||
expect((await manager.listBundles()).find(row => row.name === 'described')).toEqual({
|
||||
name: 'described', version: '2.0.0', description: 'Describes itself.', enabled: false, installed: true, optional: false, removable: true,
|
||||
meta: { title: 'described', description: 'Describes itself.' },
|
||||
rows: [{ rowId: 'described-row', moduleName }], rowSwitches: false, overrides: ['managed'],
|
||||
rows: [{ rowId: 'described-row', moduleName }], overrides: ['managed'],
|
||||
})
|
||||
await manager.setBundleEnabled('described', true)
|
||||
expect((await manager.listBundles()).find(row => row.name === 'described')?.rows).toEqual([
|
||||
@@ -411,7 +411,7 @@ it.each([
|
||||
|
||||
expect((await manager.listBundles()).find(row => row.name === 'unnamed')).toEqual({
|
||||
name: 'unnamed', version: '1.0.0', enabled: false, installed: true, optional: false, removable: true,
|
||||
rows: [], rowSwitches: true, overrides: [], ...expected,
|
||||
rows: [], overrides: [], ...expected,
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1045,7 +1045,7 @@ it('offers the launcher\'s optional bundles switched off and never removable', a
|
||||
name: offered, version: '3.0.0', description: 'Package one-liner.',
|
||||
meta: { title: offered, description: 'Package one-liner.' },
|
||||
enabled: false, installed: false, optional: true, removable: false,
|
||||
rows: [{ rowId: 'offered-row', moduleName: pathToFileURL(join(supplied, 'plugin.mjs')).href }], rowSwitches: true, overrides: [],
|
||||
rows: [{ rowId: 'offered-row', moduleName: pathToFileURL(join(supplied, 'plugin.mjs')).href }], overrides: [],
|
||||
})
|
||||
expect(await manager.setBundleEnabled(offered, true)).toMatchObject({ application: 'applied' })
|
||||
expect((await manager.listBundles()).find(row => row.name === offered)).toMatchObject({ enabled: true, optional: true, removable: false })
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
en: abc90531efa889d5
|
||||
zh: 0562570f85e69c35
|
||||
/deepseek-ai-dsh-client-ui-plugin-manager/use-this-package/switching-one-row-of-a-bundle:
|
||||
en: eafc0f85bac6a1b0
|
||||
zh: 6b51fc0cb83e5a4a
|
||||
en: 620c6a3ee20848bd
|
||||
zh: a9429f047a12bb4c
|
||||
/deepseek-ai-dsh-client-ui-plugin-manager/use-this-package/configuration-pages:
|
||||
en: c32019330fd9586d
|
||||
zh: dd22110abd10159c
|
||||
|
||||
@@ -47,7 +47,7 @@ A bundle's page shows its full package name under the title, the spec that insta
|
||||
|
||||
### Switching one row of a bundle
|
||||
|
||||
A row's switch on the bundle's page calls `pluginManager.setPluginEnabled`, which writes the row's `disabled` override into the profile's `cordis.patch.yml`. The tree recomposes at once on a profile with HMR, so the row's host half unmounts or mounts while the rest of the bundle keeps running, and the page follows the client module graph without reloading. Rows use the shared status marker for their Host fiber phase: pending and disabled are idle, loading and unloading are ongoing, active is done, and failed is error. The switch appears only on a bundle that is on; a bundle whose manifest sets `dsh.bundle.rowSwitches: false` lists its rows with their state and no switches. A row without a live entry, or one the Host will not address through the profile patch, is locked with the Host's reason. A list longer than ten rows gets a filter over localized titles, descriptions, row ids, and module names.
|
||||
A row's switch on the bundle's page calls `pluginManager.setPluginEnabled`, which writes the row's `disabled` override into the profile's `cordis.patch.yml`. The tree recomposes at once on a profile with HMR, so the row's host half unmounts or mounts while the rest of the bundle keeps running, and the page follows the client module graph without reloading. Rows use the shared status marker for their Host fiber phase: pending and disabled are idle, loading and unloading are ongoing, active is done, and failed is error. The switch appears only on a bundle that is on; a row without a live entry, or one the Host will not address through the profile patch, is locked with the Host's reason. A list longer than ten rows gets a filter over localized titles, descriptions, row ids, and module names.
|
||||
|
||||
### Configuration pages
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ Host 将网络失败或超时归因于 GitHub 地址,且提供 npmmirror 时
|
||||
|
||||
### 切换组合包里的一行
|
||||
|
||||
组合包页面上行的开关调用 `pluginManager.setPluginEnabled`,往 profile 的 `cordis.patch.yml` 写入该行的 `disabled` 覆盖。启用了 HMR 的 profile 的树随即重组,该行的宿主半区卸下或挂上,组合包其余部分照常运行,页面无需重载即跟随客户端模块图。行使用共享状态标记表示 Host fiber 阶段:pending 与 disabled 为 idle,loading 与 unloading 为 ongoing,active 为 done,failed 为 error。开关只出现在已打开的组合包上;清单设置 `dsh.bundle.rowSwitches: false` 的组合包只列出各行及其状态,不带开关。没有存活条目的行,以及 Host 不通过 profile patch 寻址的行,带着 Host 的原因锁定。超过十行的列表带一个按本地化标题、描述、行 id 和模块名筛选的输入框。
|
||||
组合包页面上行的开关调用 `pluginManager.setPluginEnabled`,往 profile 的 `cordis.patch.yml` 写入该行的 `disabled` 覆盖。启用了 HMR 的 profile 的树随即重组,该行的宿主半区卸下或挂上,组合包其余部分照常运行,页面无需重载即跟随客户端模块图。行使用共享状态标记表示 Host fiber 阶段:pending 与 disabled 为 idle,loading 与 unloading 为 ongoing,active 为 done,failed 为 error。开关只出现在已打开的组合包上;没有存活条目的行,以及 Host 不通过 profile patch 寻址的行,带着 Host 的原因锁定。超过十行的列表带一个按本地化标题、描述、行 id 和模块名筛选的输入框。
|
||||
|
||||
### 配置页
|
||||
|
||||
|
||||
@@ -188,9 +188,8 @@ function MetadataError({ error, t }: { readonly error: string | undefined; reado
|
||||
/**
|
||||
* A pack's rows as a list in the order the pack declares them: a state dot,
|
||||
* the row id, one line saying its state, a configure control for a row that
|
||||
* registered a page, and, when the pack is on and offers row switches, a
|
||||
* switch. A pack like base carries close to a hundred rows, so a long list
|
||||
* gets a filter.
|
||||
* registered a page, and, when the pack is on, a switch. A pack like base
|
||||
* carries close to a hundred rows, so a long list gets a filter.
|
||||
*/
|
||||
function RowsSection({ rows, t, resolveText, toggle, configure }: {
|
||||
readonly rows: readonly PackageRow[]
|
||||
@@ -585,7 +584,7 @@ function PackageDetail({
|
||||
rows={pkg.rows}
|
||||
t={t}
|
||||
resolveText={resolveText}
|
||||
toggle={pkg.enabled && pkg.rowSwitches ? { busy: row => busy || rowBusy(row), onSetEnabled: onSetRowEnabled } : undefined}
|
||||
toggle={pkg.enabled ? { busy: row => busy || rowBusy(row), onSetEnabled: onSetRowEnabled } : undefined}
|
||||
configure={configure}
|
||||
/>
|
||||
{renderSlot('plugins.detail.section', { subject })}
|
||||
|
||||
@@ -92,8 +92,6 @@ export interface PackageView {
|
||||
/** Why the Host cannot read the bundle, when it cannot. */
|
||||
readonly error?: ManagementError
|
||||
readonly rows: readonly PackageRow[]
|
||||
/** Whether the page offers a switch for each row while the bundle is on; false when the bundle declares its rows work only together. */
|
||||
readonly rowSwitches: boolean
|
||||
}
|
||||
|
||||
/** The typed spec as the Host read it, on the installing, installed, and failed screens. */
|
||||
@@ -414,7 +412,6 @@ export function packageView(bundle: BundleInfo, plugins: readonly PluginInfo[]):
|
||||
optional: bundle.optional,
|
||||
enabled: bundle.enabled,
|
||||
rows,
|
||||
rowSwitches: bundle.rowSwitches,
|
||||
...bundle.version === undefined ? {} : { version: bundle.version },
|
||||
...bundle.description === undefined ? {} : { description: bundle.description },
|
||||
...bundle.meta === undefined ? {} : { meta: bundle.meta },
|
||||
|
||||
@@ -35,7 +35,6 @@ function pkg(overrides: Partial<PackageView> = {}): PackageView {
|
||||
optional: false,
|
||||
enabled: true,
|
||||
rows: [],
|
||||
rowSwitches: true,
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
@@ -506,19 +505,6 @@ describe('PluginManagerPage', () => {
|
||||
expect(screen.getByRole('form', { name: 'theme settings' })).toBeTruthy()
|
||||
})
|
||||
|
||||
it('lists the rows of a bundle that declares its rows work only together with their state and no switches', () => {
|
||||
const clock = row({ rowId: 'clock', entryId: 'include:clock' as PluginEntryId, moduleName: '@acme/dsh-clock', meta: { title: { en: 'Time awareness' } } })
|
||||
renderTab({ packages: [pkg({ rows: [row(), clock], rowSwitches: false })] })
|
||||
fireEvent.click(screen.getByRole('button', { name: en.openDetail.replace('{name}', 'dsh-better-sidebar') }))
|
||||
const listed = document.querySelector('[data-plugin-row="include:clock"]') as HTMLElement
|
||||
expect(within(listed).getByText('Time awareness')).toBeTruthy()
|
||||
expect(within(listed).getByText(en.rowPhaseActive)).toBeTruthy()
|
||||
expect(document.querySelectorAll('[data-plugin-row]')).toHaveLength(2)
|
||||
// The bundle's own switch stays; its rows have none.
|
||||
expect(screen.getByRole('switch', { name: en.enableToggle.replace('{name}', 'dsh-better-sidebar') })).toBeTruthy()
|
||||
expect(within(document.querySelector('[data-plugin-rows]') as HTMLElement).queryByRole('switch')).toBeNull()
|
||||
})
|
||||
|
||||
it('shows a row id only once when it is the localized title in lists and configuration pages', () => {
|
||||
const moduleName = '@acme/dsh-sidebar/navigation'
|
||||
const { setLanguage } = renderTab(
|
||||
@@ -732,7 +718,7 @@ describe('PluginManagerPage', () => {
|
||||
})
|
||||
|
||||
it('leaves the version out of a bundle the Host reports none for', () => {
|
||||
const unversioned: PackageView = { name: 'dsh-better-sidebar', installed: true, optional: false, enabled: true, rows: [], rowSwitches: true }
|
||||
const unversioned: PackageView = { name: 'dsh-better-sidebar', installed: true, optional: false, enabled: true, rows: [] }
|
||||
renderTab({ packages: [unversioned] }, {}, bodies)
|
||||
fireEvent.click(screen.getByRole('button', { name: en.openDetail.replace('{name}', 'dsh-better-sidebar') }))
|
||||
expect(subjects.at(-1)).toEqual({ kind: 'bundle', pkg: { name: 'dsh-better-sidebar', installed: true, enabled: true, rows: [] } })
|
||||
|
||||
@@ -22,7 +22,6 @@ const BUNDLE: BundleInfo = {
|
||||
optional: false,
|
||||
removable: true,
|
||||
rows: [{ rowId: 'sidebar', moduleName: 'dsh-better-sidebar', entryId: ROW_ENTRY }, { rowId: 'theme', moduleName: 'dsh-better-sidebar/theme' }],
|
||||
rowSwitches: true,
|
||||
overrides: [],
|
||||
}
|
||||
|
||||
@@ -113,7 +112,7 @@ describe('packageView', () => {
|
||||
it('joins a bundle with the entries its rows run as', () => {
|
||||
expect(packageView(BUNDLE, PLUGINS)).toEqual({
|
||||
name: 'dsh-better-sidebar', version: '0.16.0', description: 'A sidebar.',
|
||||
installed: true, optional: false, enabled: false, rowSwitches: true,
|
||||
installed: true, optional: false, enabled: false,
|
||||
rows: [
|
||||
{ rowId: 'sidebar', moduleName: 'dsh-better-sidebar', entryId: ROW_ENTRY, enabled: true, phase: 'active' },
|
||||
{ rowId: 'theme', moduleName: 'dsh-better-sidebar/theme', enabled: false, phase: null },
|
||||
@@ -124,12 +123,11 @@ describe('packageView', () => {
|
||||
name: '@deepseek-ai/dsh-base', enabled: true, installed: false, optional: false, removable: false, readOnlyReason: 'management-required',
|
||||
error: { code: 'operation-error', diagnostic: 'broken' },
|
||||
rows: [{ rowId: 'core', moduleName: '@deepseek-ai/dsh-base', entryId: 'include:core' as PluginEntryId }, { rowId: 'gone', moduleName: 'x', entryId: 'include:gone' as PluginEntryId }],
|
||||
rowSwitches: false,
|
||||
overrides: [],
|
||||
}
|
||||
expect(packageView(protectedBundle, PLUGINS)).toEqual({
|
||||
name: '@deepseek-ai/dsh-base', installed: false, optional: false, enabled: true, readOnlyReason: 'management-required',
|
||||
error: { code: 'operation-error', diagnostic: 'broken' }, rowSwitches: false,
|
||||
error: { code: 'operation-error', diagnostic: 'broken' },
|
||||
rows: [
|
||||
{ rowId: 'core', moduleName: '@deepseek-ai/dsh-base', entryId: 'include:core', enabled: true, phase: 'active', readOnlyReason: 'management-required' },
|
||||
{ rowId: 'gone', moduleName: 'x', entryId: 'include:gone', enabled: false, phase: null },
|
||||
@@ -140,7 +138,7 @@ describe('packageView', () => {
|
||||
|
||||
describe('sortPackages', () => {
|
||||
it('orders packages by the short name a person reads, not by the Host order or enablement', async () => {
|
||||
const plain = { enabled: true, installed: true, optional: false, removable: true, rows: [], rowSwitches: true, overrides: [] }
|
||||
const plain = { enabled: true, installed: true, optional: false, removable: true, rows: [], overrides: [] }
|
||||
const zeta: BundleInfo = { ...plain, name: 'dsh-zeta' }
|
||||
const alpha: BundleInfo = { ...plain, name: '@acme/dsh-alpha', enabled: false }
|
||||
const views = [zeta, BUNDLE, alpha].map(bundle => packageView(bundle, PLUGINS))
|
||||
|
||||
@@ -4631,7 +4631,7 @@ export const TYPE_API: readonly TypeApiEntry[] = [
|
||||
},
|
||||
{
|
||||
name: 'BundleInfo',
|
||||
declaration: 'export interface BundleInfo {\n name: string;\n version?: string;\n meta?: PluginLocalizedMeta;\n description?: string;\n enabled: boolean;\n installed: boolean;\n optional: boolean;\n removable: boolean;\n readOnlyReason?: ReadOnlyReason;\n error?: ManagementError;\n rows: BundleRowInfo[];\n rowSwitches: boolean;\n overrides: string[];\n}',
|
||||
declaration: 'export interface BundleInfo {\n name: string;\n version?: string;\n meta?: PluginLocalizedMeta;\n description?: string;\n enabled: boolean;\n installed: boolean;\n optional: boolean;\n removable: boolean;\n readOnlyReason?: ReadOnlyReason;\n error?: ManagementError;\n rows: BundleRowInfo[];\n overrides: string[];\n}',
|
||||
},
|
||||
{
|
||||
name: 'BundleRowInfo',
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
en: d152484eb41ac6b4
|
||||
zh: 1db86285abf6c499
|
||||
/deepseek-ai-dsh-package-manifest/use-this-package:
|
||||
en: d0ac340eb31e948b
|
||||
zh: 64c740b5697de6ec
|
||||
en: 43fb882676bf22dd
|
||||
zh: 04e512bc4059e5d4
|
||||
/deepseek-ai-dsh-package-manifest/understand-the-implementation:
|
||||
en: 64e1e57d1c3761e0
|
||||
zh: 9fc039770ce43c63
|
||||
|
||||
@@ -43,7 +43,7 @@ const manifest: DshPackageManifest = {
|
||||
}
|
||||
```
|
||||
|
||||
`DshPackageManifest` describes the package.json fields used by DSH, with required `name` and `version`; it is not an exhaustive npm schema. Local profile readers use `Partial<DshPackageManifest>` because profiles need no published version. `DshManifest` describes only public author fields under `dsh`. `DshBundleManifest.patch` is one patch file path or an ordered list of them, each relative to the package root; the launcher applies a list in order as one bundle layer. `DshBundleManifest.rowSwitches: false` declares that the bundle's rows work only together, so the plugin manager offers no switch per row; absent means true. TypeScript checks the example and erases `import type`; these interfaces do not parse JSON or write a file.
|
||||
`DshPackageManifest` describes the package.json fields used by DSH, with required `name` and `version`; it is not an exhaustive npm schema. Local profile readers use `Partial<DshPackageManifest>` because profiles need no published version. `DshManifest` describes only public author fields under `dsh`. `DshBundleManifest.patch` is one patch file path or an ordered list of them, each relative to the package root; the launcher applies a list in order as one bundle layer. TypeScript checks the example and erases `import type`; these interfaces do not parse JSON or write a file.
|
||||
|
||||
The following metadata fields are optional. Omitting them leaves the format version or compatible host versions undeclared; readers do not infer defaults.
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ const manifest: DshPackageManifest = {
|
||||
}
|
||||
```
|
||||
|
||||
`DshPackageManifest` 描述 DSH 使用的 package.json 字段,其中 `name` 和 `version` 必填;它不是完整的 npm schema(模式)。本地 profile 读取方使用 `Partial<DshPackageManifest>`,因为 profile 无需发布版本。`DshManifest` 仅描述 `dsh` 下的公共作者字段。`DshBundleManifest.patch` 是一个 patch 文件路径,或一个有序的路径列表,均相对于包根目录;launcher 按列表顺序把它们作为同一个组合包层应用。`DshBundleManifest.rowSwitches: false` 声明该组合包的各行只能一起工作,插件管理器因此不为单行提供开关;缺省视为 true。TypeScript 检查示例并删除 `import type`;这些接口不解析 JSON,也不写入文件。
|
||||
`DshPackageManifest` 描述 DSH 使用的 package.json 字段,其中 `name` 和 `version` 必填;它不是完整的 npm schema(模式)。本地 profile 读取方使用 `Partial<DshPackageManifest>`,因为 profile 无需发布版本。`DshManifest` 仅描述 `dsh` 下的公共作者字段。`DshBundleManifest.patch` 是一个 patch 文件路径,或一个有序的路径列表,均相对于包根目录;launcher 按列表顺序把它们作为同一个组合包层应用。TypeScript 检查示例并删除 `import type`;这些接口不解析 JSON,也不写入文件。
|
||||
|
||||
以下元数据字段均可选。省略时,格式版本或兼容的宿主版本保持未声明状态;读取方不推断默认值。
|
||||
|
||||
|
||||
@@ -69,11 +69,6 @@ export interface DshEnginesManifest {
|
||||
export interface DshBundleManifest {
|
||||
/** One patch file path, or an ordered list applied in sequence, each relative to the declaring package root. */
|
||||
patch: string | string[]
|
||||
/**
|
||||
* Whether the plugin manager offers a switch for each row the bundle inserts; false for a bundle whose rows work
|
||||
* only together, which the bundle's own switch turns on and off. Absent means true.
|
||||
*/
|
||||
rowSwitches?: boolean
|
||||
}
|
||||
|
||||
/** The bundle composition declared by a profile directory. */
|
||||
|
||||
Reference in new issue
Block a user