Files
inkreach-agent/docs/plugins/04-ui-slots.md
T

119 lines
4.3 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.
# L3 · UI 槽位:胶囊、工具栏、视图、面板、iframe 页
> 前置:[L0](./01-hello-plugin.md)。宿主开放 5 个数据驱动 UI 槽位 + 1 个 iframe 页形态。**数据驱动**意味着插件只提交数据,渲染与样式由宿主统一负责——这是 UI 一致性的第一道保障(自定义视觉见 [DESIGN.md](./DESIGN.md) 与 [L4](./05-renderer-components.md))。
## 1. 状态胶囊(TitleBar 顶部)
顶栏胶囊是**紧凑状态条**,点击行为三档:
```js
// ① 纯展示
api.ui.setStatusItem('conn', { text: '已连接 · 3 任务' })
// ② 点击深链
api.ui.setStatusItem('conn', {
text: '已连接 · 3 任务',
tone: 'accent', // default | accent | warn | danger
icon: '<svg .../>', // 可选,12px 线条风
openView: { view: 'my-plugin' } // 点击跳到插件整页视图
})
// ③ 点击展开详情弹层(声明了 detail 时优先于 openView)
api.ui.setStatusItem('tasks', {
text: '任务 2/5',
tone: 'accent',
detail: {
title: '任务概览', // 缺省用胶囊文本
sections: [
{
title: '进行中', // 分组标题可省略
rows: [
{ label: '构建索引', detail: '已完成 60%', trailing: '2/5', tone: 'accent' },
{ label: '清理临时文件' }
]
},
{
title: '操作',
rows: [{ label: '打开任务面板', openView: { view: 'my-plugin' } }] // 行点击深链
}
]
}
})
api.ui.removeStatusItem('tasks')
```
弹层由宿主统一渲染(分组标题 / 行 / 右侧徽标 / 深链),样式与宿主一致——插件只提交
数据;点击弹层外部或 Esc 关闭;dispose 时宿主自动清理本插件全部条目。
## 2. 工具栏按钮(输入区工具行)
```js
api.ui.registerToolbarButton({
id: 'open',
title: '打开我的面板',
icon: '<svg .../>', // 16-20px 线条风,currentColor
openView: { view: 'my-plugin' }
})
```
## 3. 整页视图 + 侧栏入口
manifest 声明 `icon` / `order` 后,插件自动出现在侧栏(有 UI 能力时);任何槽位用 `openView` 深链:
```js
api.ui.openView('my-plugin', { section: 'detail' }) // params 透传给插件 UI
api.ui.openView('chat') // 回到聊天
```
## 4. 面板 tab(右侧 SidePanel)
manifest 加 `"panel": true`,插件在右侧面板获得一个 tab。
## 5. iframe 插件页(entry.page)
需要**完全自定义的 Web UI** 且不想构建 React 时用。物理隔离(sandbox iframe),经桥通信:
```
<插件目录>/index.html + 页面 JS
manifest: "entry": { "page": "index.html" }
```
页面侧用 SDK 桥(`@aso/plugin-sdk/client`):
```html
<script type="module">
import { getHarnessClient, useHarnessTheme, useDeepLinkParams } from './sdk-client.mjs'
const harness = getHarnessClient('my-plugin')
harness.openView('chat') // 深链
harness.setStatus('state', { text: '就绪' })
harness.invoke('getState') // 白名单:getState / send / getSettings
</script>
```
**样式统一(强制)**:插件页 `<head>` 里必须引宿主 token 表,并用自己的 Tailwind 按同样 token 名编译——
```html
<link rel="stylesheet" href="peer://assets/tokens.css" />
```
主题(深浅色/accent)由宿主推送(`useHarnessTheme`),禁止自行探测。详细规则见 [DESIGN.md](./DESIGN.md#iframe-插件页)。
## 6. 安全边界速记
| 形态 | 代码位置 | 能力 |
|---|---|---|
| main.mjs | 主进程 | 全权(与 pi 扩展同信任级) |
| renderer.mjs(L4) | 渲染层 | 宿主 React + window.api 受控面 |
| iframe 页 | 沙箱 iframe | 仅白名单桥(getState/send/getSettings/setStatus/openView) |
## 7. 验证
1. 状态胶囊:`setStatusItem` 后 TitleBar 立即出现,`removeStatusItem` 消失
2. 面板 tab:`panel: true` + 任一 UI 形态 → 右侧面板出现 tab
3. iframe 页:改页面文件 → 插件页开关触发重载 → iframe 换 src 整页刷新
4. 全部 UI 状态在日志可查(`plugin activated` 之后你在 main.mjs 里的 `api.log` 输出)
---
**上一篇** [L2](./03-settings.md) · **下一篇**:[L4 React 组件](./05-renderer-components.md)——宿主内真 React 组件。