TraceStudio-dev/docs/web1.0/FEATURE_UPDATE_v0.2.0.md
2026-01-07 19:34:45 +08:00

498 lines
12 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.

# TraceStudio 功能更新文档
## 版本记录
- **v0.2.0** - 2026-01-07
- ✨ 多层级树状分类系统
- 🖱️ 增强的右键菜单系统
- 👁️ 节点预览数据持久化
---
## 📋 更新内容
### 1. ✨ 多层级树状分类系统
#### 功能描述
节点面板支持类似文件树的多层级分类结构,方便组织大量算子。
**核心特性**
- 📁 支持最多 **3 级树状目录**
- 🏷️ 超出 3 级的分类作为 `subCategory` 标签显示
- 🔄 递归展开/折叠,自动统计子节点数量
- 🔍 搜索时自动展开匹配节点
**示例结构**
```
📥 Loader (2)
▸ CSV (1)
▸ Local (1)
📥 CSV 数据加载器
📁 File (超出3级)
▸ Trace (1)
📥 UTrace 文件加载器
⚙️ Transform (4)
▸ Filter (2)
⚙️ 行过滤器
⚙️ 时间范围过滤
▸ Select (1)
⚙️ 列选择器
▸ Aggregate (1)
⚙️ 数据聚合
```
**后端配置示例** (`server/main.py`):
```python
"CSVLoader": {
"function": "Loader",
"category": "Loader/CSV/Local/File", # 4级分类
# 显示效果:
# - Loader > CSV > Local 作为树结构
# - "File" 作为 subCategory 标签
}
"FilterRows": {
"function": "Transform",
"category": "Transform/Filter", # 2级分类正常显示
}
```
**技术实现**
- 文件:[web/src/components/NodePalette.tsx](../web/src/components/NodePalette.tsx)
- 使用 `Map<string, TreeNode>` 构建高效树索引
- 递归渲染函数 `renderTreeNode()` 处理嵌套结构
- 自动过滤空分类节点
---
### 2. 🖱️ 增强的右键菜单系统
#### 功能描述
全面的右键菜单支持,涵盖画布、节点、连线的所有常用操作。
**菜单类型**
| 右键对象 | 菜单项 | 功能描述 | 快捷键 |
|---------|-------|---------|--------|
| **画布** | 💾 保存工作流 | 保存到 localStorage | - |
| **画布** | 📥 导入工作流 | 从 JSON 文件导入 | - |
| **画布** | 📤 导出工作流 | 导出为 JSON 文件 | - |
| **画布** | 🗑️ 清空画布 | 清空所有节点和连线 | - |
| **节点** | 📋 复制节点 | 复制节点到偏移位置 | - |
| **节点** | ✂️ 断开所有连线 | 删除该节点的所有连线 | - |
| **节点** | 🗑️ 删除节点 | 删除节点及其连线 | Delete |
| **连线** | ✂️ 删除此连线 | 删除当前选中的连线 | - |
| **连线** | 🗑️ 删除此接口所有连线 | 删除同一 target handle 的所有连线 | - |
**交互优化**
- ✅ 鼠标左键拖拽时自动关闭右键菜单
- ✅ 点击画布自动关闭菜单
- ✅ 危险操作(清空画布)有确认提示
**技术实现**
- 文件:[web/src/components/Workspace.tsx](../web/src/components/Workspace.tsx)
- React Flow 事件:`onEdgeContextMenu`, `onNodeContextMenu`, `onPaneContextMenu`
- 监听拖拽开始和鼠标按下事件自动关闭菜单
**使用示例**
```typescript
// 删除同一接口的所有连线(用于清理多输入节点)
case 'deleteAll': {
const edge = edges.find(e => e.id === contextMenu.edgeId)
if (edge) {
setEdges((eds) => eds.filter((e) =>
!(e.target === edge.target && e.targetHandle === edge.targetHandle)
))
}
break
}
```
---
### 3. 👁️ 节点预览数据持久化
#### 功能描述
节点执行或预览后,预览数据会持久化显示在节点下方,无需重复加载。
**支持的预览类型**
1. **📊 表格预览**CSV 数据)
- 显示前 5 行数据
- 显示前 4 列(超出部分显示省略)
- 自动统计总行数
- 表头加粗显示
2. **🖼️ 图表预览**(图像)
- 显示图表图像
- 自适应缩放(最大高度 200px
- 支持常见图片格式
**数据结构**
```typescript
interface NodeData {
preview?: {
type: 'table' | 'image'
data: any // 表格数组或图片URL
columns?: string[] // 表格列名
}
}
```
**表格预览示例**
```json
{
"preview": {
"type": "table",
"columns": ["ID", "Name", "Value", "Timestamp"],
"data": [
{ "ID": 1, "Name": "Item A", "Value": 100, "Timestamp": "2026-01-07" },
{ "ID": 2, "Name": "Item B", "Value": 200, "Timestamp": "2026-01-07" }
]
}
}
```
**图表预览示例**
```json
{
"preview": {
"type": "image",
"data": "data:image/png;base64,iVBORw0KG..."
}
}
```
**技术实现**
- 文件:[web/src/components/nodes/UniversalNode.tsx](../web/src/components/nodes/UniversalNode.tsx)
- 预览区域使用滚动容器(最大高度 300px
- 表格使用优化的 HTML table 结构
- 图片使用 `object-fit: contain` 自适应显示
**样式特点**
- 🎨 深色主题适配
- 📏 固定宽度防止节点过宽
- 🔤 文本超出自动省略
- 📊 数据行数统计标签
---
## 🛠️ 技术细节
### 树状分类算法
**核心函数**
```typescript
const MAX_TREE_DEPTH = 3 // 最大树层级
function buildTree(): TreeNode {
const root: TreeNode = {
name: 'root',
items: [],
children: new Map(),
expanded: true
}
for (const [nodeType, meta] of Object.entries(plugins)) {
const categoryPath = meta.category || 'Other'
const parts = categoryPath.split('/')
// 限制树深度
const treeParts = parts.slice(0, MAX_TREE_DEPTH)
const subCategory = parts.length > MAX_TREE_DEPTH
? parts.slice(MAX_TREE_DEPTH).join('/')
: undefined
// 逐级构建树
let currentNode = root
for (let i = 0; i < treeParts.length; i++) {
const part = treeParts[i]
if (i === treeParts.length - 1) {
// 叶子节点
currentNode.items.push({ type: nodeType, meta, subCategory })
} else {
// 中间节点
if (!currentNode.children.has(part)) {
currentNode.children.set(part, {
name: part,
items: [],
children: new Map(),
expanded: expandedPaths.has(buildPath(treeParts.slice(0, i + 1)))
})
}
currentNode = currentNode.children.get(part)!
}
}
}
return root
}
```
**递归渲染**
```typescript
function renderTreeNode(node: TreeNode, depth: number, path: string[]): React.ReactNode {
const currentPath = buildPath(path)
const isExpanded = expandedPaths.has(currentPath) || search !== ''
// 过滤 + 统计
const filteredItems = node.items.filter(/* ... */)
const totalCount = countNodes(node)
return (
<div style={{ marginLeft: depth > 0 ? 12 : 0 }}>
{/* 目录标题 */}
{/* 叶子节点 */}
{/* 递归渲染子目录 */}
</div>
)
}
```
### 右键菜单状态管理
```typescript
const [contextMenu, setContextMenu] = useState<{
x: number
y: number
type: 'pane' | 'node' | 'edge'
nodeId?: string
edgeId?: string
} | null>(null)
// 自动关闭菜单的场景
const onPaneMouseDown = useCallback(() => {
if (contextMenu) setContextMenu(null)
}, [contextMenu])
const onNodeDragStart = useCallback(() => {
if (contextMenu) setContextMenu(null)
}, [contextMenu])
```
### 预览数据渲染
**表格优化**
- 只显示前 5 行(避免节点过长)
- 只显示前 4 列(避免节点过宽)
- 单元格内容超出自动省略
**响应式设计**
```typescript
const hasPreview = data.meta?.supports_preview && data.preview
{hasPreview && (
<div style={{
borderTop: '1px solid rgba(148,163,184,0.15)',
background: 'rgba(0,0,0,0.2)',
maxHeight: 300,
overflow: 'auto'
}}>
{/* 预览内容 */}
</div>
)}
```
---
## 📝 使用指南
### 配置多层级分类
**步骤 1修改后端配置** (`server/main.py`)
```python
"MyNode": {
"function": "Transform", # 节点功能
"category": "Transform/Filter/Advanced/Custom", # 4级分类
# 显示效果:
# Transform > Filter > Advanced (树结构)
# + "Custom" 标签 (subCategory)
}
```
**步骤 2启动服务**
```bash
# 后端
conda activate tracestudio
python -m uvicorn server.main:app --reload
# 前端
cd web
npm run dev
```
**步骤 3查看效果**
- 节点面板会自动构建树状结构
- 点击目录展开/折叠
- 使用搜索框快速定位
### 使用右键菜单
**场景 1批量删除连线**
1. 创建多个节点连接到同一个 Aggregator
2. 右键点击任意一条连线
3. 选择 "🗑️ 删除此接口所有连线"
4. 所有连接到该接口的连线都被删除
**场景 2复制节点**
1. 右键点击节点
2. 选择 "📋 复制节点"
3. 新节点会出现在偏移位置(+50px
**场景 3导出工作流**
1. 右键点击画布空白处
2. 选择 "📤 导出工作流"
3. 保存为 JSON 文件
### 添加预览数据
**方法 1执行节点后自动保存**
```typescript
// 执行完成后更新节点数据
updateNodeData(nodeId, {
preview: {
type: 'table',
columns: ['ID', 'Name', 'Value'],
data: [
{ ID: 1, Name: 'Item A', Value: 100 },
{ ID: 2, Name: 'Item B', Value: 200 }
]
}
})
```
**方法 2预览按钮触发**
```typescript
// Inspector 中添加预览按钮
<button onClick={async () => {
const result = await fetchPreview(nodeId)
updateNodeData(nodeId, {
preview: {
type: 'table',
columns: result.columns,
data: result.data.slice(0, 100) // 限制数据量
}
})
}}>
👁️ 预览数据
</button>
```
---
## 🧪 测试建议
### 测试 1多层级分类
```python
# server/main.py
"TestNode": {
"category": "A/B/C/D/E/F" # 6级分类
}
```
预期效果:显示 `A > B > C > TestNode + 标签 "📁 D/E/F"`
### 测试 2右键菜单自动关闭
1. 右键打开菜单
2. 按住鼠标左键拖拽节点
3. 菜单应该自动关闭
### 测试 3预览数据持久化
1. 给节点添加预览数据
2. 拖动节点到其他位置
3. 预览应该一直显示
4. 刷新页面后预览应该恢复(如果已持久化到 localStorage
---
## 🔧 配置选项
### 树层级限制
```typescript
// web/src/components/NodePalette.tsx
const MAX_TREE_DEPTH = 3 // 修改此值调整最大层级
```
### 预览数据限制
```typescript
// web/src/components/nodes/UniversalNode.tsx
maxHeight: 300, // 预览区域最大高度
data.slice(0, 5) // 显示前5行
columns.slice(0, 4) // 显示前4列
```
### 右键菜单样式
```typescript
// web/src/components/Workspace.tsx
background: 'rgba(10,22,40,0.95)',
backdropFilter: 'blur(12px)',
border: '1px solid rgba(59,130,246,0.2)',
```
---
## 🐛 已知问题
1. **预览数据过大**
- 问题:大数据集可能导致节点过大
- 解决方案限制预览行数当前5行
2. **树结构过深**
- 问题超过10级的分类可能显示混乱
- 解决方案:设置合理的 MAX_TREE_DEPTH推荐3-4级
3. **右键菜单位置**
- 问题:靠近边缘时菜单可能超出屏幕
- 待优化:自动调整菜单位置
---
## 📊 性能优化
1. **树结构构建**
- 使用 `Map` 数据结构,查找复杂度 O(1)
- 递归深度限制防止栈溢出
2. **预览数据渲染**
- 虚拟滚动(未来优化)
- 懒加载大图片
3. **菜单交互**
- 使用 `useCallback` 防止不必要的重渲染
- 事件委托优化性能
---
## 📚 相关文件
| 文件 | 描述 | 主要改动 |
|------|------|---------|
| `web/src/components/NodePalette.tsx` | 节点面板 | ✅ 树状分类 |
| `web/src/components/Workspace.tsx` | 画布组件 | ✅ 右键菜单 + 自动关闭 |
| `web/src/components/nodes/UniversalNode.tsx` | 通用节点 | ✅ 预览数据显示 |
| `server/main.py` | 后端API | ⚠️ 需配置 category |
---
## 🚀 未来计划
- [ ] 拖拽排序节点面板分类
- [ ] 预览数据支持更多格式JSON、Markdown
- [ ] 右键菜单支持自定义操作
- [ ] 预览数据的缓存管理
- [ ] 节点分组Group功能
- [ ] 快捷键支持Ctrl+C/V 复制粘贴)
---
## 📞 反馈与支持
如有问题或建议,请通过以下方式反馈:
- 提交 Issue
- Pull Request
- 联系开发团队
**最后更新**2026-01-07
**版本**v0.2.0