# 它是怎么接进 DSH 的 给想改代码或照着写自己插件的人。使用者只需要看 [README](../README.md)。 ## 一个包,两个面 DSH 的插件可以同时提供**宿主半**和**浏览器半**,本项目两边都用到: | 位置 | 作用 | | --- | --- | | `package.json` → `dsh.bundle.patch` | 声明这是 **Profile Bundle**。`dsh plugin --profile web add <包>` 装完后,DSH 会把这个包追加到 profile 的 `dsh.profile.bundles` 层叠里,并应用它带的 patch 文件 | | `cordis.patch.yml` | 该 bundle 的 patch 层:插入一行 Loader row(`id: whale-aquarium`、`name: dsh-whale-aquarium`) | | `lib/index.js` | **宿主半**:空的 `apply()`。存在的唯一理由是让 Loader 有个宿主侧 row 可挂;浏览器半走 `exports["./client"]` | | `package.json` → `dsh.client` + `exports["./client"]` | 声明**浏览器半**。client modules 扫描到这一行的包后,读取 `dsh.client`(`platform: "web"`、`inject`),把 `lib/client.js` 放进 `window.__DSH_BOOT__` 的 boot graph | | `lib/client.js` | 浏览器半本体:`window.__ModuleLoader__.load({ id, factory })` 注册,`factory(require)` 返回一个 Cordis 插件 `{ name, inject, apply(ctx) }` | ### 三条硬约束 1. **bundle id 必须等于包名。** client modules 用包 id 索引 factory,`factory` 里 `load({ id })` 的 `id` 与 `package.json` 的 `name` 不一致就会加载失败。 2. **浏览器半只能 `require()` 平台 seed 词**:`react`、`react/jsx-runtime`、`react-dom`、`react-dom/client`、`@deepseek-ai/cordis`、`@deepseek-ai/dsh-client-store`、`@deepseek-ai/dsh-client-ui-slots`、`@deepseek-ai/dsh-client-ui-primitives`,外加 boot graph 里其他 client 包(需要用 `dsh.client.external` 声明依赖顺序)。本项目只用了 `react`。 3. **`dsh.client.inject` 里的未知包名会被忽略**,不会让 boot 失败;它在运行时的作用是「让被注入的包先到达,再加载消费者」。 这三条都写进了 `test/smoke.mjs` 的断言,改名或结构写错会在 CI 上直接报错。 ## 注册了哪三个插槽 全部是**增量**插槽(新 id,不占用出厂 id),所以永远不会顶掉出厂 UI: | 插槽 | 用途 | | --- | --- | | `shell.overlay` | 整框浮层:在全部列之上、滚动容器之外、本身穿透点击。这里放 `` + 一个 rAF 循环 | | `sidebar.footer.action` | 侧边栏底部的一键开关(owner 会传 `wide` 表示侧边栏是展开还是 56px 轨道) | | `settings.section` | 设置面板里的一页(`id` + `order` + `label`) | 所有副作用(canvas、`pointermove`/`pointerdown` 监听、注入的 `