19 KiB
7-70 Page AI Pi Lab 原生 OpenHub-like UI 设计 v1
状态:done Owner:07-ai / mnote-web / page-ai-runtime 日期:2026-07-04
1. 结论
Pi Lab 下一阶段不做第二个 iframe,也不把 @earendil-works/pi-web-ui 或 LibreChat / Open WebUI 这类完整应用直接嵌入 MNote。正确方向是:
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 message:reasoning 折叠、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”。包内包含:
ChatPanelAgentInterfaceMessageList/MessagesMessageEditorStreamingMessageContainerThinkingBlock- 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 包含:
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 独立入口与抽屉整体
MNote page
Pi Lab floating button(default visible)
-> Pi Lab native drawer(independent 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 status:idle / 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 语义:
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.readmnote.selection.readmnote.allowed_roots.describemnote.local_file.readmnote.local_file.patchmnote.knowledge_rag.querymnote.reference.openmnote.tool_receipt.write
每个 tool card 至少显示:
- tool name
- state:pending / 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,默认摘要。
- runtime:Pi 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:
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 统一归一为:
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 provider;Pi 只能通过
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 A:Native UI v2 骨架
修改范围:
rust/crates/mnote-web/browser/sidebar-page-ai-pi-lab-runtime.jsscripts/task-pi-lab-static-smoke.jsscripts/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 B:Event / message model 收口
修改范围:
sidebar-page-ai-pi-lab-runtime.jsrust/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 C:PiClient-aligned adapter
目标:
- 增加或前端模拟
/threadscontract。 send/cancel/events对齐@assistant-ui/react-pi的语义。- 保持旧 endpoint 兼容,直到 smoke 全部迁移。
Phase D:OpenHub-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 可点击回跳。
- 当前
.mdpatch 后显示 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。