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

19 KiB
Raw Blame History

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。正确方向是:

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 包含:

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 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 labelPi Lab · 当前页面标题,恢复会话后显示短 session id。
  • runtime statusidle / starting / streaming / error。
  • model label:默认 omniroute/freefirst,可后续扩展 selector。
  • close/minimize。

不要在 topbar 放大段 scopedisabled 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.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

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 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。