4.9 KiB
4.9 KiB
L3 · UI 槽位:胶囊、工具栏、视图、面板、iframe 页
前置:L0。宿主开放 5 个数据驱动 UI 槽位 + 1 个 iframe 页形态。数据驱动意味着插件只提交数据,渲染与样式由宿主统一负责——这是 UI 一致性的第一道保障(自定义视觉见 DESIGN.md 与 L4)。
1. 状态胶囊(悬浮胶囊:最小化药丸 ⇄ 展开面板)
所有插件共用一个悬浮胶囊(标题栏右侧,ZCode 复刻)。插件照常 setStatusItem,宿主记录每次写入时间,两种形态:
- 最小化药丸:展示「最新的消息」(全部插件中
updatedAt最新的一条:tone 点 + 插件图标 + 文本;多于一条时带总数角标),点击展开面板; - 展开面板:每个插件一个专属区域——区域头(插件图标 + 名称 + 条目数)可收起/展开(记忆跨启动),区域内是该插件的条目与详情行;面板高度固定,内容太多向下滚动;点击面板外部 / Esc 收回药丸。
// ① 纯展示(只出现在药丸/区域行)
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 时区域行下展开分组明细)
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. 工具栏按钮(输入区工具行)
api.ui.registerToolbarButton({
id: 'open',
title: '打开我的面板',
icon: '<svg .../>', // 16-20px 线条风,currentColor
openView: { view: 'my-plugin' }
})
3. 整页视图 + 侧栏入口
manifest 声明 icon / order 后,插件自动出现在侧栏(有 UI 能力时);任何槽位用 openView 深链:
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):
<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 名编译——
<link rel="stylesheet" href="peer://assets/tokens.css" />
主题(深浅色/accent)由宿主推送(useHarnessTheme),禁止自行探测。详细规则见 DESIGN.md。
6. 安全边界速记
| 形态 | 代码位置 | 能力 |
|---|---|---|
| main.mjs | 主进程 | 全权(与 pi 扩展同信任级) |
| renderer.mjs(L4) | 渲染层 | 宿主 React + window.api 受控面 |
| iframe 页 | 沙箱 iframe | 仅白名单桥(getState/send/getSettings/setStatus/openView) |
7. 验证
- 状态胶囊:
setStatusItem后 TitleBar 立即出现,removeStatusItem消失 - 面板 tab:
panel: true+ 任一 UI 形态 → 右侧面板出现 tab - iframe 页:改页面文件 → 插件页开关触发重载 → iframe 换 src 整页刷新
- 全部 UI 状态在日志可查(
plugin activated之后你在 main.mjs 里的api.log输出)
上一篇 L2 · 下一篇:L4 React 组件——宿主内真 React 组件。