Skip to content

Commit b52e6bd

Browse files
committed
feat(miniapp): let MiniApps claim the floating bubble composer
Route chat.claimComposer / setComposerDraft / focusSession through the host bubble so MiniApps can own composer input and display their own agent sessions, with PPT Live as the first consumer.
1 parent 8b89e2b commit b52e6bd

22 files changed

Lines changed: 1485 additions & 1056 deletions

File tree

MiniApp/Skills/miniapp-dev/SKILL.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,9 @@ MiniApp 框架**只暴露下列能力**,没有任何"通用 BitFun 后端通
193193
| AI | `app.ai.complete / chat / cancel / getModels` | 复用宿主 AIClient,受 `permissions.ai`(含 `allowed_models` / 速率限制) |
194194
| 对话框 | `app.dialog.open/save/message` | Tauri dialog 插件 |
195195
| 剪贴板 | `app.clipboard.readText/writeText` | 宿主 navigator.clipboard |
196+
| Agent 会话 | `app.agent.run / cancel / turnText / cancelStaleRuns / onEvent` |`permissions.agent.enabled` 限制;启动小应用自己的隐藏 agent 回合,事件只回流到发起的小应用 |
197+
| 悬浮会话气泡 | `app.chat.claimComposer / releaseComposer / focusSession / setComposerDraft / onUserMessage` |`permissions.agent.enabled` 限制;把右下角悬浮会话气泡认领为小应用的输入框与过程展示(Agentic MiniApp 模式,样板间:`builtin-ppt-live`|
198+
| 幻灯片栅格化 | `app.deck.renderPage` | 在隐藏宿主 WebView 中渲染单页 HTML,返回 base64 PNG/PDF(导出用) |
196199
| 自定义后端 | `app.call('xxx', …)` + `worker.js` |`node.enabled = true` 时可用,自己实现业务逻辑 |
197200
| 主题 / i18n | `app.theme` / `app.locale` / `app.onThemeChange` / `app.onLocaleChange` / `app.t(...)` | 见对应章节 |
198201

@@ -203,7 +206,7 @@ MiniApp 框架**只暴露下列能力**,没有任何"通用 BitFun 后端通
203206
- WorkspaceService(结构化工作区索引、统一搜索)
204207
- GitService(结构化 status / diff / blame,区别于裸 `git` 命令)
205208
- TerminalService(创建/读写交互式终端)
206-
- Session / AgenticSystem(启动 Agent 会话、消费工具调用与流式事件)
209+
- Session / AgenticSystem**通用**会话管理(任意会话的创建、接管、读写)——小应用只能通过 `app.agent.*` 启动**自己的**隐藏会话并消费其事件,再通过 `app.chat.*` 借用悬浮会话气泡做输入与过程展示;不能访问其他会话
207210
- LSP / Snapshot / Mermaid / Skills / Browser API / Computer Use / Config 等
208211

209212
需要这类能力时的合规姿势:
@@ -227,6 +230,8 @@ MiniApp UI 内通过 **window.app** 访问:
227230
| `app.os.*` | 同上 |
228231
| `app.storage.*` | 同上 |
229232
| `app.dialog.open/save/message` | 由 Bridge 转 Tauri dialog 插件 |
233+
| `app.agent.*` | 隐藏 agent 回合(`permissions.agent.enabled`),事件经 `app.agent.onEvent` 回流 |
234+
| `app.chat.*` | 悬浮会话气泡集成:`claimComposer({ placeholder })` 认领气泡输入框(当前 tab 为本小应用时,用户输入经 `onUserMessage` 送达)、`focusSession(sessionId)` 让气泡展示本小应用的 agent 会话、`setComposerDraft(text)` 展开气泡并预填输入(不发送,供示例 prompt 使用)|
230235
| 生命周期 / 事件 | 见 bridge_builder 生成的适配器 |
231236

232237
## 主题集成

src/crates/contracts/product-domains/src/miniapp/bridge_builder.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use serde_json;
55

66
/// Build the Runtime Adapter script (JS) to inject into the iframe.
77
/// Exposes window.app with call(), fs.*, shell.*, net.*, os.*, storage.*, dialog.*,
8-
/// ai.*, agent.*, deck.*, clipboard.*, lifecycle, events.
8+
/// ai.*, agent.*, deck.*, chat.*, clipboard.*, lifecycle, events.
99
pub fn build_bridge_script(
1010
app_id: &str,
1111
app_data_dir: &str,
@@ -135,6 +135,24 @@ pub fn build_bridge_script(
135135
renderPage: (opts) => _rpc('deck.renderPage', opts || {{}}),
136136
}},
137137
138+
// Chat namespace — floating session bubble integration for agentic
139+
// MiniApps. While this MiniApp's tab is active, `claimComposer` routes the
140+
// bubble composer to the MiniApp: user messages arrive via
141+
// 'chat:userMessage' instead of being sent to the host chat session, and
142+
// `focusSession` shows one of the MiniApp's own agent.run sessions in the
143+
// bubble so agent progress renders on the shared chat surface.
144+
// Requires manifest permissions.agent.enabled = true; enforced host-side.
145+
chat: {{
146+
claimComposer: (opts) => _rpc('chat.claimComposer', opts || {{}}),
147+
releaseComposer: () => _rpc('chat.releaseComposer', {{}}),
148+
focusSession: (sessionId) => _rpc('chat.focusSession', {{ sessionId }}),
149+
// Opens the bubble and prefills its composer without sending, so the
150+
// MiniApp can offer example prompts the user still edits and submits.
151+
setComposerDraft: (text) => _rpc('chat.setComposerDraft', {{ text }}),
152+
onUserMessage: (fn) => app.on('chat:userMessage', fn),
153+
offUserMessage: (fn) => app.off('chat:userMessage', fn),
154+
}},
155+
138156
// Clipboard namespace — proxies to host navigator.clipboard (bypasses sandbox restriction).
139157
clipboard: {{
140158
writeText: (text) => _rpc('clipboard.writeText', {{ text }}),

src/crates/contracts/product-domains/src/miniapp/builtin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ pub const BUILTIN_APPS: &[BuiltinMiniAppBundle] = &[
151151
},
152152
BuiltinMiniAppBundle {
153153
id: "builtin-ppt-live",
154-
version: 252,
154+
version: 253,
155155
meta_json: include_str!("builtin/assets/ppt-live/meta.json"),
156156
html: include_str!("builtin/assets/ppt-live/index.html"),
157157
css: include_str!("builtin/assets/ppt-live/style.css"),

src/crates/contracts/product-domains/src/miniapp/builtin/assets/ppt-live/README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,43 @@
22

33
PPT Live 是 BitFun 的内置 MiniApp,用于 AI 驱动的 PPT 生成、编辑和导出。
44

5+
## Agentic MiniApp 样板间:复用悬浮会话气泡
6+
7+
PPT Live 同时是 BitFun **Agentic MiniApp** 的样板间:它自己**没有输入框,也没有
8+
过程显示**——右侧栏只有样式设置和一张引导卡。用户在右下角的悬浮会话气泡里描述
9+
需求,链路如下:
10+
11+
```
12+
用户在悬浮气泡输入
13+
→ FloatingMiniChat 检测到当前 tab 的 MiniApp 持有 composer claim
14+
→ window CustomEvent 'miniapp-composer-message'
15+
→ useMiniAppBridge 转成 iframe 事件 'chat:userMessage'
16+
→ ui.js submitInstruction(text):包装 ppt-design 协议 prompt,走原有
17+
app.agent.run 隐藏会话(首轮建会话,后续编辑复用同一 sessionId)
18+
→ executeBackendTurn 拿到 sessionId 后调用 app.chat.focusSession(sessionId)
19+
→ 气泡的 ChatPane 切到该会话——agent 的执行过程直接显示在气泡里
20+
→ agent 按文件协议写 project.json / slides/*.html,PPT Live 渐进式读文件上屏
21+
```
22+
23+
涉及的 `app.chat.*` API(`bridge_builder.rs` 生成,宿主端在
24+
`web-ui/src/app/scenes/miniapps/hooks/useMiniAppBridge.ts`,需要
25+
`permissions.agent.enabled = true`):
26+
27+
| API | 作用 |
28+
|-----|------|
29+
| `app.chat.claimComposer({ placeholder })` | 认领气泡输入框;本应用 tab 激活时用户输入改送本应用。幂等 upsert,locale 变更时重调可更新占位文案 |
30+
| `app.chat.onUserMessage(fn)` | 接收气泡输入,payload 为 `{ text }` |
31+
| `app.chat.focusSession(sessionId)` | 让气泡的会话面板展示本应用自己的 agent 会话(仅限本应用 `agent.run` 启动的会话) |
32+
| `app.chat.setComposerDraft(text)` | 展开气泡并预填输入框,**不发送**——欢迎页的示例 prompt 用它,用户仍可编辑后再发 |
33+
| `app.chat.releaseComposer()` | 主动释放;iframe 卸载时宿主自动释放 |
34+
35+
> 认领是按 **runner 实例**(token)而不是 appId 记账的:AI 定制时同一个 appId 会同时挂载
36+
> 已安装实例和草稿预览实例,若按 appId 路由,一条气泡消息会让两个 iframe 各跑一次 agent。
37+
38+
为什么这是好实践:PPT Live 在用户输入需求后本来就是启动一个 agent 会话去完成
39+
任务——与其在 MiniApp 里再造一套输入框和过程流水线,不如把输入和过程都交给宿主
40+
现成的会话表面,MiniApp 只专注于自己的领域视图(画布、样式、导出)。
41+
542
## 目录结构
643

744
```
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"schemaVersion": 1,
33
"id": "builtin-ppt-live",
4-
"version": 252
4+
"version": 253
55
}

0 commit comments

Comments
 (0)