Files
inkreach-agent/docs/plugins/05-renderer-components.md
T

97 lines
4.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# L4 · 宿主内 React 组件(entry.renderer)
> 前置:[L3](./04-ui-slots.md)。iframe 页是隔离的「贴进去的网页」;`entry.renderer` 则是**真正的宿主内 React 组件**——与内置插件同槽位、同观感、同主题。这是外置插件的最高 UI 形态。
## 1. 声明
```json
{ "id": "my-plugin", "panel": true, "entry": { "renderer": "renderer.mjs" } }
```
> 文件名**不要叫 index.ts/index.js**——那是 pi 原生扩展的自动发现名,会被 pi CLI 双重加载。
## 2. renderer.mjs 契约
宿主动态 import 后调用默认导出 `mount(ctx)`:
```js
export default function mount(ctx) {
// ctx.react 是宿主的 React 单实例——必须用它创建组件(自带 React 副本会让 hooks 全部失效)
const R = ctx.react
function Panel() {
const [n, setN] = R.useState(0)
return R.createElement('div', { className: 'p-3 space-y-2' },
R.createElement('div', { className: 'text-[12.5px] text-ink' }, `点击 ${n} 次`),
R.createElement('button', {
className: 'rounded-control border border-line-strong px-2.5 py-1 text-[11.5px] text-ink hover:bg-hover',
onClick: () => setN(n + 1)
}, '+1')
)
}
ctx.registerPanel(Panel) // 右侧面板 tab 内容(需 manifest.panel: true)
ctx.registerView(Panel) // 整页视图(侧栏入口 → 主内容区);两个都注册也可以
}
```
## 3. JSX 工程构建配方(推荐)
手写 `createElement` 只适合玩具;真实插件用 JSX + esbuild,构建时把 `react` 别名到**宿主全局实例**:
```sh
esbuild renderer.tsx --bundle --format=esm --outfile=renderer.mjs \
--jsx=automatic \
--alias:react=./shims/react.js --alias:react/jsx-runtime=./shims/jsx-runtime.js \
--alias:@aso/plugin-sdk=<sdk源码路径>/index.ts --alias:@aso/plugin-sdk/ui=<sdk源码路径>/ui.tsx
```
> 最后两条 alias 只在用到 `@aso/plugin-sdk`(withTimeout 等)或 `./ui` 组件库时需要;
> sdk 以源码形态发布(exports 指向 .ts/.tsx),esbuild 无法按包名解析,指路径即可。
```js
// shims/react.js —— 从宿主全局取单例
const R = window.__ASO_PLUGIN_HOST__.react
export default R
export const { useState, useEffect, useRef, useMemo, useCallback, createContext,
useContext, Fragment, memo, forwardRef, createElement } = R
```
```js
// shims/jsx-runtime.js
export const { jsx, jsxs, Fragment } = window.__ASO_PLUGIN_HOST__.jsxRuntime
```
`packages/plugin-template/` 已内置这套配置,复制即用。
## 4. 硬性约束
| 约束 | 原因 |
|---|---|
| React 必须来自 `ctx.react` / 宿主全局 | 双 React 实例 → hooks 全部报错 |
| 模块**自包含**(单文件 bundle) | 宿主以 Blob URL 装载,相对导入没有基路径 |
| 不 import 裸包名(react 等) | 同上;一切依赖打进 bundle(react 走别名除外) |
| 单文件 ≤ 8MB | 超限拒绝装载 |
| 仅开发版装载 | 用户版第三方装载关闭(`ASO_EXTERNAL_PLUGINS=1` 豁免) |
## 5. 样式
组件直接跑在宿主树里——**宿主的 Tailwind 类与 token 全部可用**。标准模式已代码化:优先 `import { Button, SettingRow, LoadingState, ErrorState, EmptyState, Switch } from '@aso/plugin-sdk/ui'`(组件目录与三态铁律见 [L6](./07-ui-components.md)),不要手写;遵守 [DESIGN.md](./DESIGN.md) 的类名词汇,禁止硬编码色值/阴影。
## 6. 生命周期
- 装载:插件列表变化(启停/重载/启动)即重装;`mount` 同步执行注册
- 卸载:插件禁用/移除 → 注册的组件自动从宿主卸载
- 性能护栏:读源与 import 各限时 8s——超时/失败时槽位呈现宿主统一「装载失败」ErrorState(带重试),见 [L7](./08-performance.md)
- 崩溃隔离:渲染错误被 `PluginErrorBoundary` 捕获,不影响宿主
## 7. 验证
1. 重载插件 → 插件页该行出现 ⚙(若已声明设置)/侧栏入口/面板 tab
2. 控制台无 `[plugins] … renderer 装载失败`
3. 点击组件内按钮,state 正常更新(hooks 生效 = 单实例约束满足)
---
**上一篇** [L3](./04-ui-slots.md) · **下一篇**:[L6 公共组件库](./07-ui-components.md)——别手写样式,宿主把标准模式做成了组件。