Files
mnote/design/07-ai/done/7-70-page-ai-pi-lab-native-openhub-like-ui-v2.md
T

500 lines
19 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.
# 7-70 Page AI Pi Lab 原生 OpenHub-like UI 设计 v1
状态:done
Owner07-ai / mnote-web / page-ai-runtime
日期:2026-07-04
## 1. 结论
Pi Lab 下一阶段不做第二个 iframe,也不把 `@earendil-works/pi-web-ui` 或 LibreChat / Open WebUI 这类完整应用直接嵌入 MNote。正确方向是:
```text
MNote Page AI 独立 Pi Lab 浮动入口 + 原生抽屉 UI
-> 体验参考当前 OpenHub AI 面板,但入口和容器不依赖 OpenHub
-> 运行协议参考 @assistant-ui/react-pi 的 PiClient / HTTP SSE thread contract
-> 消息、composer、tool、artifact 形态参考 Pi 官方 web-ui 与 ai-elements
-> 权限、知识库、receipt、watcher refresh 继续由 MNote 托管
```
OpenHub 继续作为默认 Page AI 主线。Pi Lab 使用独立浮动按钮与独立抽屉容器,默认在 MNote 页面中显示;目标是把 7-69 的 debug spike UI 升级成可验证的 MNote-native Page AI 体验,而不是替代或复用 OpenHub 的入口。若后续确认不需要 Pi Lab,直接退役这一独立入口和 runtime 即可,不需要先用 feature flag 隔离。
## 2. 原则
- **独立入口默认显示**:Pi Lab 新增自己的悬浮按钮和抽屉容器,默认显示,不复用当前 OpenHub 的按钮、iframe 页面或 provider tab 容器;以后退役 OpenHub 或 Pi Lab 任一侧,都不影响另一侧。
- **原生承载**Pi Lab UI 渲染在 MNote 自有 DOM 中,不新增独立产品页作为主验收面,不 iframe 第二个完整 AI 应用。
- **优先参考和复用**:能复用当前 OpenHub 已证明的交互、Pi 官方 UI 的事件语义、assistant-ui 的 runtime contract、ai-elements 的组件形态,就不要自造一套孤立协议。
- **MNote 拥有真相**session binding、allowed roots、LightRAG facade、tool receipt、file patch、citation/open-reference、watcher refresh 仍归 MNote;第三方 UI 不能接管这些事实源。
- **Debug 后置**Page AI 首屏必须是对话、输入、上下文和变更结果;runtime health、scope JSON、receipt 原文只进折叠诊断区或开发态右栏。
- **不破坏 OpenHub**OpenHub 仍是默认入口和默认 Page AI 主线;Pi Lab 作为独立入口与 OpenHub 并列但互不依赖,不出现普通用户可见的 fallback/迁移暗示。
## 3. 参考资源取证
### 3.1 当前 OpenHub UI
本地 OpenHub 是当前最接近目标体验的产品基线:
- `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/pages/SmartQueryPage.jsx`
- `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/components/ChatInput.jsx`
- `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/components/AssistantMessage.jsx`
- `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/components/ToolCall.jsx`
- `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/components/HistoryDrawer.jsx`
- `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/components/DiffViewer.jsx`
- `/mnt/Data1T/Mnote_data/openhub/OpenHub/smart-query-frontend/src/components/KnowledgeManager.jsx`
可参考能力:
- 顶部/底部紧凑控制区:agent mode、model select、当前页面/当前文件夹按钮。
- 主消息流:user / assistant bubble、streaming、retry / undo、idle state。
- assistant messagereasoning 折叠、tool summary、tool cards、citation chips。
- session/history:历史抽屉、恢复会话、归档。
- changed files / diff:隐藏索引桥接 MNote,用户侧展示 changed files / diff 入口。
- knowledge:知识库入口和 citation 回跳模式。
不直接复用方式:
- 不把 OpenHub React/AntD 组件直接搬进 `mnote-web` 主运行时。
- 不复制 OpenHub session/message 全文存储作为 Pi Lab 真相。
- 不复用 OpenHub iframe shell 实现 Pi Lab,因为用户目标是 MNote-native。
### 3.2 Pi 官方 web-ui
`@earendil-works/pi-web-ui@0.75.3` 已核验为 MIT 包,描述为 “Reusable web UI components for AI chat interfaces powered by @earendil-works/pi-ai”。包内包含:
- `ChatPanel`
- `AgentInterface`
- `MessageList` / `Messages`
- `MessageEditor`
- `StreamingMessageContainer`
- `ThinkingBlock`
- tool renderers
- artifacts runtime / sandbox
- `ModelSelector`
- IndexedDB storage stores
可参考能力:
- Pi 消息流与工具流的视觉分层。
- thinking / tool / artifact 的独立组件边界。
- composer 与 model/thinking selector 的组合方式。
- artifacts-style 侧栏。
不直接整包接入原因:
- 默认假设 browser 侧持有 `Agent`、provider keys、IndexedDB session/settings。
- 默认可暴露 Pi 自带 tools / attachments / artifacts 事实源。
- 与 MNote 后端托管 Pi RPC/SSE、AiAccessScope、MNote-owned tools 的边界冲突。
迁移条件:
- 先提供 remote-session adapter,使 `ChatPanel` / `AgentInterface` 不直接管理 provider key、IndexedDB 和 `Agent.state.tools`
- 所有 tool call、file read/write、knowledge query、receipt 仍必须走 MNote facade。
### 3.3 assistant-ui / react-pi
`@assistant-ui/react-pi@0.0.5` 已核验为 MIT,说明其 browser entry 是 RPC-isomorphic `PiClient`,通过 HTTP/SSE 驱动 Pi-backed threads。其 contract 包含:
```text
GET /threads
POST /threads
GET /threads/:id
PATCH /threads/:id
POST /threads/:id/messages
POST /threads/:id/cancel
GET /models
POST /threads/:id/model
POST /threads/:id/thinking
GET /threads/:id/events
```
可参考能力:
- thread / message / composer / queue / cancel 的协议边界。
- snapshot-first SSE reconnect 语义。
- model / thinking controls。
- host UI approval requests。
- tool-associated approval / interrupt 的 UI 映射。
当前不直接引 React UI 的原因:
- `mnote-web` 当前主路径是 Rust SSR + browser JS,不是 React app。
- 直接引入 React island、Tailwind/shadcn 体系会扩大构建和运行时边界。
- 当前优先目标是把 MNote-owned Pi adapter 做完整,而不是重开前端框架主线。
设计决策:**先对齐 contract,不先引 UI 框架**。后续如果 MNote 需要 React island,再以该 contract 为迁移桥。
### 3.4 ai-elements
`ai-elements@1.9.0` 已核验为 Apache-2.0,定位是基于 shadcn/ui 的 AI-native component registry。
可参考能力:
- message / conversation / prompt input / reasoning / tool / source / response card 的组件形态。
- 更现代的 AI chat 视觉密度和状态表达。
当前不直接接入原因:
- 依赖 React、Tailwind、shadcn registry 和 CSS variables。
- 对当前 MNote Rust SSR + browser JS 主路径引入成本偏高。
设计决策:作为视觉和组件拆分参考,不作为 7-70 第一阶段依赖。
### 3.5 完整应用类参考
LibreChat、Open WebUI、AnythingLLM、Continue、Cline、Roo Code 只作为交互模式参考:
- LibreChat / Open WebUI / AnythingLLM:参考历史会话、多模型、RAG、文件区、设置入口。
- Continue / Cline / Roo Code:参考 agent tool timeline、审批、diff、changed files、任务状态。
不作为直接依赖或嵌入目标。
## 4. UI 目标结构
### 4.1 独立入口与抽屉整体
```text
MNote page
Pi Lab floating buttondefault visible
-> Pi Lab native drawerindependent from OpenHub iframe/page
topbar
Pi Lab title
session title
runtime status
close / minimize
context strip
current page chip
selection chip
allowed roots chip
LightRAG chip
model / thinking selector
body
message timeline
optional right rail
composer
prompt input
context buttons
send / abort / queue state
folded diagnostics
```
桌面端允许右侧 rail;移动端不显示右 rail,改为底部 sheet 或折叠 section。
入口规则:
- Pi Lab 使用独立悬浮按钮,例如右下角 `π`,默认渲染。
- Pi Lab 点击后打开独立抽屉,不借用 OpenHub 当前按钮、OpenHub iframe 页或 OpenHub provider tab。
- Pi Lab 抽屉可以视觉参考 OpenHub,但 DOM、状态、session、事件订阅、关闭/最小化都独立。
- OpenHub 的现有按钮和页面保持不变;Pi Lab 不在 OpenHub 面板内新增 tab 作为主入口。
- 若未来退役 OpenHub,Pi Lab 浮动入口仍可保留;若退役 Pi Lab,OpenHub 入口不受影响。
- `MNOTE_PAGE_AI_PI_LAB` 可保留为临时开发/强制关闭开关,但不是产品入口显示的前置条件。
### 4.2 Topbar
目标是接近 OpenHub 的 AI 面板,而不是 debug 工具页。
必须包含:
- Pi Lab 标题与实验标记;不放 OpenHub/Pi provider tabs 作为主导航。
- session label`Pi Lab · 当前页面标题`,恢复会话后显示短 session id。
- runtime statusidle / starting / streaming / error。
- model label:默认 `omniroute/freefirst`,可后续扩展 selector。
- close/minimize。
不要在 topbar 放大段 `scope``disabled builtins`、raw env 等诊断文案。
### 4.3 Context Strip
替代当前偏 debug 的工具列表,参考 OpenHub `ChatInput` 中当前页面/文件夹按钮:
- 当前页:真实 `.md` 路径或页面标题,点击在 MNote 中定位。
- 选区:无选区时 disabled,有选区时显示字符数。
- allowed roots:显示 root 名称和数量,点击展开只读详情。
- LightRAG:显示 provider 状态;query citation 走 `mnote.reference.open`
- 文件变更:当前 session 有 patch 后显示 changed files chip。
Context strip 是用户可理解的任务上下文,不展示 JSON。
### 4.4 Message Timeline
消息模型要吸收 OpenHub `AssistantMessage` 与 Pi/assistant-ui 的 part 语义:
```text
MNotePiUiMessage
id
role = user | assistant | system
status = queued | streaming | done | error | aborted
createdAt
model
parts[]
MNotePiUiPart
text
reasoning
toolCall
toolResult
citation
diff
fileChange
approval
error
```
展示规则:
- user message 右侧气泡。
- assistant message 左侧主体块,正文优先。
- reasoning 默认折叠,严禁把 provider `reasoning_content` 混入 final answer。
- tool timeline 默认折叠成 summary,运行中自动展开当前 tool。
- tool 输入/输出长 JSON 默认收起,显示摘要和 deny reason。
- citation 显示为来源 chip,点击走 MNote open-reference。
- diff / fileChange 显示为 changed file chips + diff summary,点击走 MNote document pane / diff view。
- error 明确显示 provider/runtime/tool deny,不伪装成功。
### 4.5 Tool Timeline
参考 OpenHub `ToolCall.jsx`、Pi official tool renderers、Cline/Roo 的工具审批流。
第一阶段必须覆盖:
- `mnote.current_page.read`
- `mnote.selection.read`
- `mnote.allowed_roots.describe`
- `mnote.local_file.read`
- `mnote.local_file.patch`
- `mnote.knowledge_rag.query`
- `mnote.reference.open`
- `mnote.tool_receipt.write`
每个 tool card 至少显示:
- tool name
- statepending / running / allowed / denied / error / done
- compact params summary
- deny reason
- receipt id
- file version before / after
- diff summary
- citation count
默认禁用 Pi 原始 `bash/read/write/edit` 的状态可以放在折叠详情里,不占据首屏。
### 4.6 Composer
参考 OpenHub `ChatInput`、Pi `MessageEditor`、assistant-ui composer queue 语义。
第一阶段:
- 多行输入。
- icon-only 当前页、选区、文件夹/allowed roots、LightRAG 按钮,带 tooltip 和 aria-label。
- send 按钮。
- streaming 时显示 abort;若后端支持 queue,再允许 follow-up queue。
- 发送前自动附带 MNote context envelope,不把上下文 URL 或 JSON 写入 textarea。
- 禁用态要有明确原因:未启动 runtime、missing model、missing auth、scope denied。
后续阶段:
- model selector。
- thinking level selector。
- thread queue / steer。
- prompt template / skill picker。
### 4.7 Right Rail / Drawer
桌面端右侧 rail 承接“有用但不该挤占主对话”的信息:
- session/history:当前只显示当前 session 与最近 session,完整历史后续做抽屉。
- changed files:变更文件列表、patch count、打开按钮。
- citations:本轮 citation 列表。
- receipts:最近 tool receipts,默认摘要。
- runtimePi PID / provider session id / runtime mode,只在 dev mode 显示。
移动端右 rail 不常驻,改为按钮展开底部 sheet。
### 4.8 Diagnostics
折叠 diagnostics 只面向开发:
- raw SSE events tail。
- allowed roots raw snapshot。
- receipt raw JSON。
- runtime command / pid / session dir。
- disabled builtin tools。
默认折叠,不参与普通用户验收截图。
## 5. 后端与协议设计
### 5.1 保留现有 7-69 endpoint
当前 7-69 已有:
- `/api/page-ai/pi/status`
- `/api/page-ai/pi/bootstrap`
- `/api/page-ai/pi/start`
- `/api/page-ai/pi/send`
- `/api/page-ai/pi/abort`
- `/api/page-ai/pi/events`
- `/api/page-ai/pi/tool-call`
- `/page-ai/pi`
7-70 不要求立即破坏这些 endpoint。
但前端入口要调整为默认显示的独立浮动按钮与独立 drawer host`/page-ai/pi` 可保留为 debug/internal 直达页,不作为产品主验收入口。
### 5.2 新增 PiClient-aligned adapter 层
为了后续能接 `@assistant-ui/react-pi` 或复用其 reducer 语义,新增 provider-neutral adapter contract
```text
GET /api/page-ai/pi/threads
POST /api/page-ai/pi/threads
GET /api/page-ai/pi/threads/:id
POST /api/page-ai/pi/threads/:id/messages
POST /api/page-ai/pi/threads/:id/cancel
GET /api/page-ai/pi/threads/:id/events
GET /api/page-ai/pi/models
POST /api/page-ai/pi/threads/:id/model
POST /api/page-ai/pi/threads/:id/thinking
```
兼容策略:
- 第一阶段可在前端 adapter 中把旧 endpoint 映射为 thread contract。
- 后端可逐步增加新 endpoint;旧 smoke 不被破坏。
- SSE 必须 snapshot-first:重连先发当前 thread snapshot,再发 live events。
- browser disconnect 不等于 abort;只有 cancel/abort 明确停止 runtime。
### 5.3 UI event normalization
Pi RPC / mock / future SDK event 统一归一为:
```text
thread_snapshot
message_created
message_delta
message_completed
reasoning_delta
tool_call_started
tool_call_updated
tool_call_completed
tool_call_denied
citation_added
file_patch_applied
receipt_written
runtime_status_changed
thread_error
```
所有 UI 只消费 normalized events,不直接依赖 Pi JSONL 原始字段。
## 6. 数据与权限边界
- `AiAccessScope` / allowed roots 是所有 file tool 的唯一授权来源。
- Pi Lab UI 只显示 allowed roots 摘要,不允许用户从 UI 临时扩大 root。
- `mnote.local_file.read` / `patch` 必须返回 normalized path、root id、deny reason、file version。
- LightRAG 继续是唯一默认 knowledge providerPi 只能通过 `mnote.knowledge_rag.query` 查询。
- citation 点击必须走 `mnote.reference.open` / MNote open-reference,不直接让 Pi 打开文件。
- receipt 当前仍可落 provider-neutral adapter,但 UI v2 必须按 Turso/libSQL 迁移目标设计字段。
- 文件 patch 后必须通过 MNote watcher / document refresh 链路更新 tiptap;禁止新增轮询刷新。
## 7. 实施阶段
### Phase ANative UI v2 骨架
修改范围:
- `rust/crates/mnote-web/browser/sidebar-page-ai-pi-lab-runtime.js`
- `scripts/task-pi-lab-static-smoke.js`
- `scripts/task-pi-lab-browser-smoke.js`
- 必要时补 `rust/crates/mnote-web/src/ssr/styles/components/page-ai.css`
目标:
- 当前 Pi Lab 从 debug shell 升级为 OpenHub-like 抽屉。
- Pi Lab 使用独立悬浮按钮打开独立抽屉,不复用 OpenHub 按钮、OpenHub iframe 页面或 provider tab。
- 首屏只保留对话、context strip、composer。
- tool receipts / diagnostics 默认后置。
- 截图必须显示:在 MNote 当前页面中通过独立 `π` 浮动按钮打开 Pi Lab 原生抽屉,非 OpenHub iframe、非独立页、非 OpenHub provider tab。
### Phase BEvent / message model 收口
修改范围:
- `sidebar-page-ai-pi-lab-runtime.js`
- `rust/crates/mnote-web/src/routes/page_ai_pi.rs`
- smoke 脚本
目标:
- 新增 normalized event reducer。
- reasoning、tool、citation、diff、receipt 不再混杂在一条 debug 文本里。
- SSE reconnect snapshot-first。
- abort/cancel 明确区分 browser disconnect。
### Phase CPiClient-aligned adapter
目标:
- 增加或前端模拟 `/threads` contract。
- `send` / `cancel` / `events` 对齐 `@assistant-ui/react-pi` 的语义。
- 保持旧 endpoint 兼容,直到 smoke 全部迁移。
### Phase DOpenHub-like 功能补齐
目标:
- session history 抽屉。
- changed files rail。
- citation list。
- diff preview。
- model / thinking selector。
- tool approval / host UI requests。
### Phase E:第三方组件复用决策
进入条件:
- Phase A-D 浏览器截图和 smoke 通过。
- 当前 MNote native UI 已形成稳定 message/event model。
决策:
- 若 MNote 仍保持非 React 主路径:继续原生实现,只参考 Pi web-ui / ai-elements。
- 若引入 React island:优先评估 `assistant-ui + @assistant-ui/react-pi`,并复用 Phase C contract。
- 若官方 Pi web-ui 提供 remote-session adapter:评估局部复用 `AgentInterface` / `Messages` / `MessageEditor`,不得引入 browser provider key 和 IndexedDB 作为 MNote 真相。
## 8. 验收标准
- OpenHub 默认入口不变。
- MNote 默认显示独立 Pi Lab 浮动按钮;点击后打开独立 Pi Lab 原生抽屉。
- Pi Lab 不复用 OpenHub 按钮、OpenHub 页面或 OpenHub iframe 容器。
- UI 首屏接近 OpenHub:消息流、上下文、输入框、模型/状态、tool/citation/diff 摘要齐全。
- 不出现第二个 iframe AI 应用。
- 可发送 prompt、接收 stream、abort。
- reasoning 折叠且不泄漏到 final answer。
- tool call 显示状态、deny reason、receipt id。
- allowed roots 越界读写在 UI 中显示 denied。
- LightRAG citation 可点击回跳。
- 当前 `.md` patch 后显示 changed file,并通过 watcher refresh 当前 tiptap 页。
- diagnostics 默认折叠。
- browser smoke 输出截图。
## 9. 明确非目标
- 不替换 OpenHub。
- 不把 Pi Lab 做成普通用户默认入口。
- 不 iframe LibreChat / Open WebUI / AnythingLLM / Pi web-ui demo app。
- 不把 Pi Lab 做成 OpenHub 面板内部的 provider tab 或复用 OpenHub 页面容器。
- 不把 React / shadcn / Tailwind 作为 7-70 Phase A 必需依赖。
- 不开放 Pi 原始 bash、无约束 read/write/edit。
- 不新增第二套 RAG。
- 不把 OpenHub 或 Pi 的 session JSONL 作为 MNote control-plane 长期真相。
## 10. 当前后续任务
- [ ] Phase A:重做 `sidebar-page-ai-pi-lab-runtime.js` 信息架构、独立浮动入口与视觉层级。
- [ ] Phase A:更新 browser smoke,截图验证独立 `π` 浮动按钮打开 OpenHub-like Pi Lab 抽屉。
- [ ] Phase B:抽出 normalized event reducer。
- [ ] Phase C:补 PiClient-aligned thread adapter。
- [ ] Phase D:补 changed files / diff / citation rail。
- [ ] Phase E:重新评估是否引入 `assistant-ui` 或 Pi official remote-session adapter。