mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-30 04:15:45 +08:00
docs: add Page component tutorial (zh + en)
Add comprehensive Page component documentation covering: - Use cases (admin panels, data displays, interactive tools) - Manifest file format - Backend handler with PageRequest/PageResponse - Frontend development with Page SDK (API calls, dark mode, i18n) - Full FAQ Manager example - Update add.mdx and docs.json navigation for both languages Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
2281e4aea0
commit
3d1d97ecca
@@ -9,6 +9,7 @@ title: "添加组件"
|
||||
- 工具(Tool):供 LangBot 内置的 Local Agent 在执行期间由 LLM 调用。
|
||||
- RAG 引擎(KnowledgeEngine):为 LangBot 提供知识库的索引和检索能力,管理文档摄取、检索和删除。
|
||||
- 解析器(Parser):在文档摄取前将 PDF、Word 等二进制文件解析为结构化文本,供 RAG 引擎使用。
|
||||
- 页面(Page):在 LangBot WebUI 侧边栏中注册自定义可视化页面,提供管理面板、数据展示等交互式界面。
|
||||
|
||||
## 添加组件
|
||||
|
||||
@@ -75,3 +76,4 @@ components:
|
||||
- [Tool](./tool)
|
||||
- [KnowledgeEngine](./knowledge-engine)
|
||||
- [Parser](./parser)
|
||||
- [Page](./page)
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
---
|
||||
title: "组件:页面"
|
||||
---
|
||||
|
||||
Page 组件允许插件在 LangBot 的 WebUI 侧边栏中注册自定义的可视化页面。页面运行在 iframe 沙箱中,通过 Page SDK 与宿主通信,并可调用插件后端 API。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- **管理面板**:为插件提供可视化的配置和数据管理界面(如 FAQ 管理、数据统计仪表盘)
|
||||
- **数据展示**:展示插件运行时的统计数据、日志、图表等
|
||||
- **交互式工具**:提供表单、编辑器等交互式界面
|
||||
|
||||
## 添加页面组件
|
||||
|
||||
在插件目录执行命令:
|
||||
|
||||
```bash
|
||||
lbp comp Page
|
||||
```
|
||||
|
||||
根据提示输入页面名称后,会在 `components/pages/` 目录下生成页面文件:
|
||||
|
||||
```bash
|
||||
├── components
|
||||
│ └── pages
|
||||
│ └── dashboard
|
||||
│ ├── dashboard.yaml # 页面清单
|
||||
│ ├── dashboard.py # 页面后端处理程序
|
||||
│ ├── index.html # 页面入口
|
||||
│ └── i18n # 翻译文件(可选)
|
||||
│ ├── en_US.json
|
||||
│ └── zh_Hans.json
|
||||
```
|
||||
|
||||
同时,`manifest.yaml` 中会添加组件发现配置:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
components:
|
||||
Page:
|
||||
fromDirs:
|
||||
- path: components/pages/
|
||||
maxDepth: 2
|
||||
```
|
||||
|
||||
## 清单文件:页面
|
||||
|
||||
```yaml
|
||||
apiVersion: v1 # 请勿修改
|
||||
kind: Page # 请勿修改
|
||||
metadata:
|
||||
name: dashboard # 页面唯一 ID,同一插件内不可重复
|
||||
label:
|
||||
en_US: Dashboard # 页面显示名称,显示在 WebUI 侧边栏,支持多语言
|
||||
zh_Hans: 仪表盘
|
||||
spec:
|
||||
path: index.html # HTML 入口文件,相对于该 YAML 所在目录的路径
|
||||
execution:
|
||||
python:
|
||||
path: dashboard.py # 页面后端处理程序
|
||||
attr: DashboardPage # 处理程序类名
|
||||
```
|
||||
|
||||
## 后端处理
|
||||
|
||||
Page 组件的后端处理程序继承 `Page` 基类,实现 `handle_api` 方法来处理来自前端页面的 API 请求。
|
||||
|
||||
```python
|
||||
from langbot_plugin.api.definition.components.page import Page, PageRequest, PageResponse
|
||||
|
||||
|
||||
class DashboardPage(Page):
|
||||
|
||||
async def handle_api(self, request: PageRequest) -> PageResponse:
|
||||
# request.endpoint: API 端点路径,如 '/stats'
|
||||
# request.method: HTTP 方法(GET、POST、PUT、DELETE)
|
||||
# request.body: 请求体(已解析的 JSON,或 None)
|
||||
|
||||
if request.endpoint == '/stats' and request.method == 'GET':
|
||||
# 通过 self.plugin 访问插件实例的共享状态
|
||||
return PageResponse.ok({
|
||||
'total': len(self.plugin.entries),
|
||||
})
|
||||
|
||||
return PageResponse.fail(f'Unknown endpoint: {request.endpoint}')
|
||||
```
|
||||
|
||||
### PageRequest 字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `endpoint` | `str` | API 端点路径(如 `'/entries'`) |
|
||||
| `method` | `str` | HTTP 方法(`GET`、`POST`、`PUT`、`DELETE`) |
|
||||
| `body` | `Any` | 请求体(已解析的 JSON,或 `None`) |
|
||||
|
||||
### PageResponse 构造
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `PageResponse.ok(data)` | 成功响应,`data` 为任意可 JSON 序列化的数据 |
|
||||
| `PageResponse.fail(error)` | 失败响应,`error` 为错误信息字符串 |
|
||||
|
||||
## 前端页面开发
|
||||
|
||||
在 HTML 文件中引入 Page SDK,即可与插件后端通信:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
body {
|
||||
background: var(--langbot-bg, #ffffff);
|
||||
color: var(--langbot-text, #0a0a0a);
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
padding: 24px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 data-i18n="title">Dashboard</h1>
|
||||
<p id="stats"></p>
|
||||
|
||||
<script src="/api/v1/plugins/_sdk/page-sdk.js"></script>
|
||||
<script>
|
||||
langbot.onReady(async function(ctx) {
|
||||
// 调用后端 API
|
||||
var data = await langbot.api('/stats', null, 'GET');
|
||||
document.getElementById('stats').textContent = 'Total: ' + data.total;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Page SDK API
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| `langbot.onReady(callback)` | SDK 就绪后触发回调,`callback` 接收 `ctx` 参数(包含 `theme` 和 `language`) |
|
||||
| `langbot.api(endpoint, body?, method?)` | 调用插件后端 `handle_api`,返回 Promise |
|
||||
| `langbot.t(key, fallback?)` | 获取翻译字符串 |
|
||||
| `langbot.onThemeChange(callback)` | 主题变更回调 |
|
||||
| `langbot.onLanguageChange(callback)` | 语言变更回调 |
|
||||
| `langbot.applyI18n()` | 手动重新应用 `data-i18n` 翻译 |
|
||||
|
||||
### 暗色模式
|
||||
|
||||
SDK 会自动在页面上设置 CSS 自定义属性,直接使用即可:
|
||||
|
||||
| CSS 变量 | 用途 |
|
||||
|------|------|
|
||||
| `--langbot-bg` | 页面背景色 |
|
||||
| `--langbot-bg-card` | 卡片背景色 |
|
||||
| `--langbot-text` | 主文字色 |
|
||||
| `--langbot-text-muted` | 次要文字色 |
|
||||
| `--langbot-border` | 边框色 |
|
||||
| `--langbot-accent` | 强调色 |
|
||||
|
||||
### 页面 i18n
|
||||
|
||||
在页面目录下创建 `i18n/` 目录,放入 JSON 翻译文件:
|
||||
|
||||
```
|
||||
pages/dashboard/
|
||||
├── index.html
|
||||
└── i18n/
|
||||
├── en_US.json
|
||||
└── zh_Hans.json
|
||||
```
|
||||
|
||||
翻译文件格式为扁平 JSON 键值对:
|
||||
|
||||
```json
|
||||
{
|
||||
"title": "仪表盘",
|
||||
"totalEntries": "条目总数"
|
||||
}
|
||||
```
|
||||
|
||||
在 HTML 元素上添加 `data-i18n` 属性,SDK 会自动替换文本:
|
||||
|
||||
```html
|
||||
<h1 data-i18n="title">Dashboard</h1>
|
||||
```
|
||||
|
||||
## 完整示例:FAQ 管理器
|
||||
|
||||
以下是一个完整的 Page 组件示例,实现了 FAQ 条目的增删改查。
|
||||
|
||||
**后端处理程序**(`components/pages/manager/manager.py`):
|
||||
|
||||
```python
|
||||
from langbot_plugin.api.definition.components.page import Page, PageRequest, PageResponse
|
||||
|
||||
|
||||
class ManagerPage(Page):
|
||||
|
||||
async def handle_api(self, request: PageRequest) -> PageResponse:
|
||||
plugin = self.plugin
|
||||
|
||||
if request.endpoint == '/entries' and request.method == 'GET':
|
||||
return PageResponse.ok({'entries': plugin.entries})
|
||||
|
||||
if request.endpoint == '/entries' and request.method == 'POST':
|
||||
question = (request.body or {}).get('question', '').strip()
|
||||
answer = (request.body or {}).get('answer', '').strip()
|
||||
if not question or not answer:
|
||||
return PageResponse.fail('question and answer are required')
|
||||
entry = plugin.add_entry(question, answer)
|
||||
await plugin.persist()
|
||||
return PageResponse.ok({'entry': entry})
|
||||
|
||||
if request.endpoint == '/entries' and request.method == 'DELETE':
|
||||
entry_id = (request.body or {}).get('id', '')
|
||||
if plugin.delete_entry(entry_id):
|
||||
await plugin.persist()
|
||||
return PageResponse.ok({'deleted': entry_id})
|
||||
return PageResponse.fail('entry not found')
|
||||
|
||||
return PageResponse.fail(f'Unknown: {request.method} {request.endpoint}')
|
||||
```
|
||||
|
||||
**前端页面**(`components/pages/manager/index.html`)中通过 `langbot.api()` 调用后端:
|
||||
|
||||
```javascript
|
||||
// 加载条目
|
||||
var data = await langbot.api('/entries', null, 'GET');
|
||||
|
||||
// 添加条目
|
||||
await langbot.api('/entries', { question: '...', answer: '...' }, 'POST');
|
||||
|
||||
// 删除条目
|
||||
await langbot.api('/entries', { id: '...' }, 'DELETE');
|
||||
```
|
||||
|
||||
完整示例代码请参考 [FAQManager 插件](https://github.com/langbot-app/langbot-plugin-demo/tree/main/FAQManager)。
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 页面运行在 `sandbox="allow-scripts allow-forms"` 的 iframe 中,不能弹窗或导航父页面
|
||||
- 通过 `self.plugin` 访问插件实例的共享状态,Page 和 Tool 组件可以共享数据
|
||||
- 使用 `PageResponse.ok()` 和 `PageResponse.fail()` 构造响应,确保格式一致
|
||||
- Page SDK 的 `<script>` 标签必须放在使用 `langbot` 对象的代码之前
|
||||
Reference in New Issue
Block a user