> ## Documentation Index
> Fetch the complete documentation index at: https://docs.qomicex.top/llms.txt
> Use this file to discover all available pages before exploring further.

# 插件依赖与互调用

## 插件依赖与互调用

插件可以依赖其他插件（前置插件），并在插件之间互相调用方法。典型的场景：MarkdownLib 作为库插件提供 `renderMarkdown` 方法，其他插件依赖它并在页面里调用。

## 一、声明依赖

在 manifest 的 `dependencies` 字段声明：

```json theme={null}
{
  "id": "top.qomicex.assistant",
  "dependencies": [
    { "id": "top.qomicex.markdown", "version": ">=1.0.0" },
    { "id": "top.qomicex.themes", "version": ">=1.0 <2.0", "optional": true }
  ]
}
```

| 字段 | 说明 |
| - | - |
| `id` | 被依赖插件的 manifest id |
| `version` | 版本范围（语法见 [manifest 详解](https://docs.qomicex.top/plugins/manifest.md#版本范围语法)），缺省=任意 |
| `optional` | `true` 可选前置；缺省 `false` = 必装前置 |

## 二、必装前置（optional: false）

* **安装时**：缺失或版本不满足 → 拒绝安装，后端返回 `PLUGIN_MISSING_DEPENDENCY`(400)，错误消息列出缺失项
* **启动时**：前置插件未安装或未启用 → 该插件**跳过激活并置为 disabled**
* 用户需先安装前置插件，才能安装依赖它的插件

## 三、可选前置（optional: true）

* 安装 / 激活**不强制检查**
* 仅当前置插件存在且已启用时，`callPlugin` 才能调用其方法
* 前置缺失时调用 `callPlugin` 报错，但插件本身正常启用
* 调用可选前置的方法时**务必 try/catch**：

```js theme={null}
try {
  const result = await __PLUGIN_API__.callPlugin('top.qomicex.themes', 'applyTheme', 'dark')
} catch (e) {
  // 可选前置缺失或未激活
}
```

## 四、库插件惯例（被依赖方）

**库插件（如 MarkdownLib）通常：**

* **不声明 `contributes.menuItems`** → 不会在侧边栏创建任何入口
* 只声明 `entry.frontend`（哪怕是一个空白页）+ 激活时 `registerMethod`
* `permissions` 至少包含 `config:write`（`registerMethod` 需要）

```json theme={null}
{
  "id": "top.qomicex.markdown",
  "name": "MarkdownLib",
  "version": "1.2.0",
  "minLauncherVersion": "0.1.0",
  "layers": ["l3"],
  "permissions": ["config:write"],
  "entry": { "frontend": "dist/index.html" }
}
```

库插件的 `layers` 用纯 `["l3"]`（内联）或 `["l2", "l3"]`（沙箱）均可。要点：

* 激活取决于 `entry.frontend` 是否存在 + 用户是否启用，与 layers 无关
* 必须声明 `entry.frontend`：只有声明了 frontend 的插件才会被激活（从而执行脚本、注册方法）。省略 frontend 的插件不会被激活，方法也就无法注册
  见 [layers 图层定义](https://docs.qomicex.top/plugins/manifest.md#layers-图层定义)。

## 五、提供方：注册方法

被依赖插件在激活后的页面脚本中注册方法：

```html theme={null}
（MarkdownLib dist/index.html）
<!DOCTYPE html>
<html>
<head>
（marked 必须随包分发（CSP 会拦截外部 CDN 脚本，见「开发要点与避坑指南」））
  <script src="marked.min.js"></script>
</head>
<body>
  <div id="root">MarkdownLib 已加载</div>
  <script>
    __PLUGIN_API__.registerMethod('renderMarkdown', function (md) {
      return marked.parse(md || '')
    })

    __PLUGIN_API__.registerMethod('stripHtml', function (html) {
      const div = document.createElement('div')
      div.innerHTML = html
      return div.textContent || ''
    })
  </script>
</body>
</html>
```

* `registerMethod(method, fn)`：`fn` 返回值为值或 Promise（异步方法同样支持）
* 一个插件可注册多个方法
* 插件停用时自动注销全部方法
* 权限：`config:write`

## 六、调用方：调用方法

```html theme={null}
（AI 助手 dist/index.html）
<!DOCTYPE html>
<html>
<body>
  <div id="out"></div>
  <script>
    async function render() {
      try {
        const html = await __PLUGIN_API__.callPlugin('top.qomicex.markdown', 'renderMarkdown', '**加粗**')
        document.getElementById('out').innerHTML = html
      } catch (e) {
        document.getElementById('out').textContent = '渲染失败: ' + e.message
      }
    }
    render()
  </script>
</body>
</html>
```

* 调用签名：`callPlugin(插件Id, 方法名, ...参数)`
* 权限：`network:fetch`
* 支持调用异步方法（提供方返回 Promise 时直接 await）

## 七、激活顺序

启动器启动时对被依赖插件**先排序激活**（拓扑排序 `sortByDependencies`），保证依赖方激活时前置插件已就绪并注册好方法。因此无需关心 manifest 里的声明顺序。

## 八、错误处理

| 场景 | 错误信息 |
| - | - |
| 目标插件未安装 / 未激活 / 方法未注册 | `插件 xxx 未提供方法 yyy（可能未安装或未激活）` |
| 可选前置缺失 | 同上，但插件本身正常启用 |
| 方法内部抛错 | `插件 xxx 方法 yyy 执行失败: <具体错误>` |

## 完整流程

* 用户安装 MarkdownLib → 成功（无依赖）
* 用户安装 AI 助手 → 后端检查依赖：markdown 已装且 `1.2.0 >= 1.0.0` → 成功
* 启动器启动 → 先激活 MarkdownLib（注册 `renderMarkdown`）→ 再激活 AI 助手
* AI 助手 `callPlugin('top.qomicex.markdown', 'renderMarkdown', md)` → 主窗口 `__pluginRegistry` 中转 → 返回渲染结果


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.