# L3 · UI 槽位:胶囊、工具栏、视图、面板、iframe 页 > 前置:[L0](./01-hello-plugin.md)。宿主开放 5 个数据驱动 UI 槽位 + 1 个 iframe 页形态。**数据驱动**意味着插件只提交数据,渲染与样式由宿主统一负责——这是 UI 一致性的第一道保障(自定义视觉见 [DESIGN.md](./DESIGN.md) 与 [L4](./05-renderer-components.md))。 ## 1. 状态胶囊(悬浮胶囊:最小化药丸 ⇄ 展开面板) 所有插件**共用一个悬浮胶囊**,悬浮于 main 区域右上角(侧面板打开时自动左移让位;ZCode 复刻)。插件照常 `setStatusItem`,宿主记录每次写入时间,两种形态: - **最小化药丸**:展示「最新的消息」(全部插件中 `updatedAt` 最新的一条:tone 点 + 插件图标 + 文本;多于一条时带总数角标),点击展开面板; - **展开面板**:面板头就是插件名(第一个区域的右侧是收回药丸的 ⤡);多个插件时各占一个区域、区域头可收起/展开(记忆跨启动)。区域内 `detail` 的**分组是手风琴**——默认收起只显示「分组标题 + 行尾徽标」,点击展开 rows(ZCode 式「计划 ›」);无标题分组直接平铺。面板高度固定,内容太多向下滚动;点击面板外部 / Esc 收回药丸。 ```js // ① 纯展示(只出现在药丸/区域行) api.ui.setStatusItem('conn', { text: '已连接 · 3 任务' }) // ② 点击深链 api.ui.setStatusItem('conn', { text: '已连接 · 3 任务', tone: 'accent', // default | accent | warn | danger icon: '', // 可选,12px 线条风 openView: { view: 'my-plugin' } // 点击跳到插件整页视图 }) // ③ 携详情(分组 = 手风琴:收起显示 标题+trailing 徽标,点击展开 rows) 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: '', // 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 ``` **样式统一(强制)**:插件页 `
` 里必须引宿主 token 表,并用自己的 Tailwind 按同样 token 名编译—— ```html ``` 主题(深浅色/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 组件。