Skip to content

Commit c3b00fc

Browse files
authored
feat(rich-content): deliver R0-R4 rich content foundation
feat(rich-content): deliver R0-R4 rich content foundation
2 parents 8d040f1 + 677504b commit c3b00fc

71 files changed

Lines changed: 6566 additions & 20 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ name: CI
22

33
on:
44
push:
5-
branches: [main, master]
5+
# Phase-gated feature branches must receive the same remote validation as
6+
# the protected integration branches before their next stage can begin.
7+
branches: ["**"]
68
pull_request:
79
branches: [main, master]
810

docs/design/frontend-ui-guidelines.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
- **按钮、下拉菜单、Popover、Select、Dialog、Tooltip 等控件的细节与变体**:编写或调整时须同时对照 [button-menu-design-spec.md](./button-menu-design-spec.md)
99
- **可复刻参考(CodePilot)**[`docs/ui/02-chat.md`](../ui/02-chat.md)[`docs/ui/03-workspace.md`](../ui/03-workspace.md)[`docs/ui/04-settings.md`](../ui/04-settings.md)[`docs/ui/06-markdown-message-tools.md`](../ui/06-markdown-message-tools.md)(视觉与能力对齐;IA 以 shell 规范本期边界为准)。
1010

11-
**最后审阅 / Last reviewed:** 2026-08-07(v32
11+
**最后审阅 / Last reviewed:** 2026-08-09(v34
1212

1313
## 1. 设计理念 (Design Philosophy)
1414

@@ -207,6 +207,15 @@ MisakaX 的目标是打造一个**现代化、专业、克制的桌面端 Agent
207207
- 主列已由 `MessageList``max-w-3xl` 约束;Assistant **不再**`surface-card` 底色卡片。
208208
- 禁止用卡片底与 User 气泡混淆。
209209

210+
### 4.6.x.1 富内容块(Rich Content Blocks)
211+
212+
- 富内容是 Assistant 正文中的**有序内容块**,不是新的整条消息气泡;Assistant 外层仍保持无背景、无圆角外壳。无块或功能开关关闭时必须回退到既有 Markdown 正文。
213+
- 每个独立块使用 `my-4 rounded-xl border-border/40 bg-muted/20` 的低对比容器,头部使用 `size-4` Lucide 图标、`text-sm font-medium` 标题和紧凑 `icon-xs` 操作;禁止 dashboard 式强色背景、悬停位移或缩放。
214+
- 图表、地图、文件预览必须惰性加载,并提供受控的文本/数据/下载降级路径;块级失败只能显示局部 notice,不能中断相邻 Markdown、工具调用或消息 footer。
215+
- 文件预览使用共享 `Dialog`,始终标明“只读”;二进制资源仅经窄 IPC 获取,禁止在 JSX 注入 HTML、任意 URL、`file:` 路径或未验证 SVG。所有可见标签、状态、`aria-label` 和错误文案必须走 `chat.richContent.*` i18n key。
216+
- 图表和地图的辅助操作(数据表、要素列表、复制、导出)必须键盘可达;颜色不是唯一信息来源,库加载/WebGL 失败时显示等价文本数据。
217+
- R4a 地图只渲染经校验的本地 GeoJSON:不得接收 tile URL、外部样式或其他远程资源;地图区域保持中性底色、固定可读高度和加载态,渲染失败时回退为要素列表。长要素列表默认最多显示 200 项,并明确告知截断。
218+
210219
### 4.6.y 工具调用状态行(ToolActionsGroup)
211220

212221
- 主路径为左色线分组 + 紧凑行;pending 用静态圆点(`size-2 rounded-full bg-muted-foreground/40`),running 用 **中性** spinner(`text-muted-foreground`,禁止蓝色品牌 spinner)。
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# 富内容与产物交付总体实施方案
2+
3+
> **用途:** 定义聊天富内容能力的目标架构、实施顺序、边界和验收标准。
4+
> **受众:** 产品、架构、全栈开发和安全维护者。
5+
> **最后审阅 / Last reviewed:** 2026-08-09
6+
> **状态:** R0–R4 已实现并通过远程 CI;R5、R6 仍作为后续计划。本文件保留总体基线与验收方向。
7+
8+
---
9+
10+
## 1. 目标与范围
11+
12+
用户与 Agent 对话时,助手回复除 Markdown 外还应能在聊天流内显示并操作:
13+
14+
1. 统计图:柱状、折线、面积、散点、饼/环、条形、指标卡及可访问的数据表替代。
15+
2. 地图:首期显示结构化点、线、面、GeoJSON 和视口;后续支持受控瓦片与交互。
16+
3. 生成文件:在应用内预览支持的格式,并将原始文件保存到用户指定位置。
17+
4. 全模态模型图片:展示、放大和下载模型生成的图片。
18+
5. 保持已有 Markdown、代码、表格、Mermaid、数学公式、工具调用、流式显示、会话分页/导入导出行为不回退。
19+
20+
首期不做任意网页嵌入、任意 JavaScript 图表、远程 Office 在线查看器、云端文件托管或通用文件管理器。这些选项会扩大 WebView、网络和隐私攻击面,不能作为“在线查看”的默认实现。
21+
22+
## 2. 当前基线与主要缺口
23+
24+
|| 已有能力 | 富内容缺口 |
25+
|---|---|---|
26+
| React | `Streamdown` 已渲染 Markdown、Mermaid、数学公式;`MessageItem` 已展示用户图片附件 | 助手消息只有 `content: string`;没有可插拔块渲染、产物工具栏、文件预览或图片输出状态 |
27+
| 流式事件 | `stream_token``stream_thinking``stream_complete`、工具事件 | 只能推送文本增量;没有块完成、产物就绪、预览失败等事件 |
28+
| Rust/SQLite | `messages` 保存正文、附件 JSON、思考和工具调用;自定义 `fs_*` command 已有工作区 root containment | 没有产物身份、内容哈希、生命周期、预览派生物、授权下载、保留策略和块顺序 |
29+
| 模型链路 | Rig 具备图片输入;Sidecar 已预留流式协议 | 未把模型/工具的图表、地图、文件、图片标准化为可验证的输出契约 |
30+
| 安全 | 主 WebView 已取消通用 FS/HTTP/Shell 权限,CSP 为本地严格策略 | 需要为受控本地产物增加窄 URI/asset 范围;不能重新打开宽权限 |
31+
32+
以上判断来自当前 `src/lib/ipc/types.ts``use-stream-listener.ts``services/llm/backend.rs``MessageItem.tsx``fs_explorer.rs``tauri.conf.json` 的实际代码;详细证据见 [08-research-sources.md](./08-research-sources.md)
33+
34+
## 3. 总体决策
35+
36+
### 3.1 用“内容块”替代 Markdown 指令和中间件链
37+
38+
采用版本化的 `ContentBlock` 判别联合,而不是让模型在 Markdown 中嵌入自定义 HTML、代码围栏约定或任意 JSON。每个 assistant 消息按顺序包含 `markdown``chart``map``artifact``image``notice` 等块;未知版本或未知类型必须安全降级为提示卡和原始元数据下载,不执行内容。
39+
40+
前端采用 **Renderer Registry(注册表)** 按块类型寻找渲染器;每个渲染器只接收已验证 DTO。相比 middleware,中间件更适合处理请求/响应横切流程,不能自然表达“消息中第 N 个独立可持久化对象”的生命周期、重试、无障碍替代和懒加载。
41+
42+
### 3.2 用受控 ArtifactService 管理所有二进制与文件
43+
44+
图像、生成文件、预览缩略图、PDF 页面位图等不进入 `messages.content`,也不把完整 Base64 长期塞进消息 JSON。Rust 侧的 `ArtifactService` 负责:接收、配额与 MIME 侦测、内容哈希、原子落盘、预览任务、读取授权、导出保存、清理和审计。消息只持久化 `artifact_id` 和不可变摘要。
45+
46+
### 3.3 首期选择“安全数据渲染”,不执行模型生成代码
47+
48+
- 图表接收受限的、无函数的规范化数据/编码,而不是原生 ECharts `Option` 或 HTML/JavaScript。
49+
- 地图首期接收 GeoJSON/marker 的数据子集;瓦片来源只能引用管理员/用户已配置的 `tile_source_id`,不能由模型给出任意 URL。
50+
- 文件预览按 MIME Strategy 选择本地解析器;不调用在线 Office Viewer,也不把用户文件上传到第三方。
51+
- 图片仅放行 raster MIME(PNG/JPEG/WebP/GIF);SVG/HTML 等活跃内容默认作为下载文件,不内联到主聊天 WebView。
52+
53+
### 3.4 把“显示安全”与“执行 Sandbox”分层
54+
55+
受控文件存储、WebView URI 范围、CSP、内容 schema 和解析资源限制是本功能必需的 **内容安全面**。完整 OS Sandbox 是执行面:当 Agent、Skill 或 MCP 运行外部命令、复杂转换器或不可信二进制时,后续必须接入现有 `SandboxBroker`。二者不能互相替代。
56+
57+
## 4. 目标架构
58+
59+
```mermaid
60+
flowchart LR
61+
MODEL["LLM / Agent / MCP tool"] --> NORMALIZE["Output adapter + schema validation"]
62+
NORMALIZE --> BLOCKS["MessageBlockService"]
63+
NORMALIZE --> ARTIFACTS["ArtifactService"]
64+
ARTIFACTS --> STORE["App-owned artifact store\nhash + quota + lifecycle"]
65+
BLOCKS --> DB[("SQLite\nmessages + message_blocks + artifacts")]
66+
ARTIFACTS --> DB
67+
BLOCKS --> EVENTS["Tauri domain events"]
68+
EVENTS --> CHAT["React message store"]
69+
DB --> CHAT
70+
CHAT --> REGISTRY["Renderer Registry"]
71+
REGISTRY --> MD["Markdown renderer"]
72+
REGISTRY --> CHART["Chart renderer"]
73+
REGISTRY --> MAP["Map renderer"]
74+
REGISTRY --> PREVIEW["Artifact preview strategy"]
75+
REGISTRY --> IMAGE["Image viewer"]
76+
PREVIEW --> URI["Scoped artifact URI / read command"]
77+
IMAGE --> URI
78+
ARTIFACTS --> EXPORT["Dialog save + Rust copy"]
79+
AGENTEXEC["Agent / Skill external execution"] -. "future, brokered" .-> SANDBOX["SandboxBroker + network policy"]
80+
```
81+
82+
## 5. 推荐实施顺序
83+
84+
| 阶段 | 可见交付 | 关键依赖 | 是否阻塞完整 Sandbox |
85+
|---|---|---|---|
86+
| R0 | 统一 DTO、Schema、迁移、feature flag、安全测试基线 | 现有聊天回归测试 ||
87+
| R1 | 文件/图片产物的可靠保存、展示元数据、下载 | ArtifactService、窄 IPC/URI ||
88+
| R2 | PDF、文本、表格等本地预览与失败回退 | 解析器 Strategy、资源限制 | 否;高风险转换器后接 Sandbox |
89+
| R3 | 受限图表块、数据表替代、导出 | Renderer Registry、ECharts adapter ||
90+
| R4 | 静态 GeoJSON 地图;受控瓦片试点 | Map renderer、地图数据策略 | 否;远程瓦片需网络/CSP policy |
91+
| R5 | Sidecar/Agent/MCP 规范化产出、权限与审计 | Phase 4 对话迁移、MCP loop | 部分依赖现有 Sandbox 计划 |
92+
| R6 | 安全加固、跨平台验证、清理和发布 | R0–R5、Sandbox 平台能力 | 是,涉及外部执行时 |
93+
94+
详细工作包与退出条件在 [01-phased-module-practice-plan.md](./01-phased-module-practice-plan.md)
95+
96+
## 6. 文件系统与 Sandbox 结论
97+
98+
### 6.1 是否需要文件系统能力
99+
100+
需要,但必须限定为 **Rust Core 的应用专属产物目录****用户显式选择的导出路径**
101+
102+
- Rust 可使用 `std::fs`/`tokio::fs`,并通过自定义 command 验证 `artifact_id`、会话归属、大小、哈希、允许 MIME 和保存目标。
103+
- React 不安装 `@tauri-apps/plugin-fs`,不获得 `$HOME`、工作区或任意路径 read/write scope。
104+
- WebView 展示通过 scoped `asset:` 或自定义 `misakax-artifact:` URI;路径不能来自模型或前端字符串。
105+
- 工作区中由 Agent 创建的文件先快照/复制入产物目录,再作为 Artifact 引用;不得向 WebView 暴露工作区原始路径。
106+
107+
### 6.2 是否需要 Sandbox
108+
109+
不需要把完整 OS Sandbox 作为上述静态展示的上线门槛;它不能解决 HTML/XSS、错误 MIME、解压炸弹或 WebView 权限问题。需要在计划中新增以下衔接:
110+
111+
- R0 建立 `ContentSafetyPolicy`,包含文件大小、类型、解析时间/页数、像素、嵌套压缩和 URI 授权。
112+
- R2 对 CPU/内存密集或原生二进制转换器采用独立受限 worker;在 Sandbox Provider 可用前,不可用时应拒绝预览并保留下载。
113+
- R5/R6 将 Agent/Skill/MCP 的外部生成过程接入 `SandboxBroker`;网络地图瓦片走受控 provider/broker,不让 WebView 任意直连。
114+
115+
完整矩阵与理由见 [05-filesystem-and-sandbox-research.md](./05-filesystem-and-sandbox-research.md)
116+
117+
## 7. 成功标准
118+
119+
1. 旧消息和纯 Markdown 消息渲染无变化,新块在流式与历史加载中顺序稳定。
120+
2. 所有可下载产物有 ID、原文件名、MIME、字节数、SHA-256、创建来源和保留状态;导出后校验哈希一致。
121+
3. 主 WebView 不拥有通用文件系统、HTTP 或 Shell 权限;错误 ID、路径猜测和任意 `file:` URL 均不能读取产物。
122+
4. 图表和地图只渲染结构化数据;模型输出的脚本、远程 iframe、任意 tile URL 和内联 SVG 不能执行。
123+
5. 每个图表提供可访问摘要和表格数据;地图提供要素列表/坐标文本;所有操作可键盘触达。
124+
6. 大文件、未知 MIME、损坏文件、解析超时、磁盘不足、过期引用均以可理解的本地化错误降级,不导致会话丢失或崩溃。
125+
7. Agent/MCP 产物的来源、用户导出和策略拒绝均进入结构化审计;涉及外部程序时符合既有 Sandbox 策略。
126+
127+
## 8. 风险与非目标
128+
129+
| 风险 | 策略 |
130+
|---|---|
131+
| 模型返回伪造/恶意结构 | 只接收工具/adapter 产出的 schema;Rust 再验证;未知块 fail closed |
132+
| Base64/大 JSON 使 SQLite、导出和滚动变慢 | 二进制离库;块/预览懒加载;内容哈希去重与配额 |
133+
| 文件解析器遭遇畸形文件或资源耗尽 | 类型 allowlist、魔数检测、页/行/像素/时间上限;必要时 worker + Sandbox |
134+
| 地图瓦片带来隐私、密钥和 CSP 扩张 | 静态 GeoJSON 优先;受控 tile source、Rust broker、缓存和 attribution |
135+
| 组件注册表沦为散乱 if/else | 明确渲染器端口、能力声明、契约测试与 feature flag |
136+
| 直接修改既有 `content` 造成历史损坏 | 新表/dual-read/dual-write,迁移完成后再切换读路径 |
137+
138+
## 9. 决策门
139+
140+
开始 R1 前必须确认:受控 URI 选择(窄 `asset:` scope 或自定义协议)和产物存储保留策略。
141+
142+
开始 R2 前必须确认:首期预览 MIME allowlist、单文件/会话/总量配额及 PDF/Office 解析方案许可证。
143+
144+
开始 R4 前必须确认:是否有合法的地图数据/瓦片服务、隐私提示、attribution、缓存上限和在线/离线行为。
145+
146+
开始 R5 前必须确认:Sidecar 已从 501 占位迁移至真实对话链路;外部执行是否已满足 Sandbox Provider 的对应平台 gate。

0 commit comments

Comments
 (0)