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

12 KiB
Raw Blame History

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):

"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
  • 使用 Map<string, TreeNode> 构建高效树索引
  • 递归渲染函数 renderTreeNode() 处理嵌套结构
  • 自动过滤空分类节点

2. 🖱️ 增强的右键菜单系统

功能描述

全面的右键菜单支持,涵盖画布、节点、连线的所有常用操作。

菜单类型

右键对象 菜单项 功能描述 快捷键
画布 💾 保存工作流 保存到 localStorage -
画布 📥 导入工作流 从 JSON 文件导入 -
画布 📤 导出工作流 导出为 JSON 文件 -
画布 🗑️ 清空画布 清空所有节点和连线 -
节点 📋 复制节点 复制节点到偏移位置 -
节点 ✂️ 断开所有连线 删除该节点的所有连线 -
节点 🗑️ 删除节点 删除节点及其连线 Delete
连线 ✂️ 删除此连线 删除当前选中的连线 -
连线 🗑️ 删除此接口所有连线 删除同一 target handle 的所有连线 -

交互优化

  • 鼠标左键拖拽时自动关闭右键菜单
  • 点击画布自动关闭菜单
  • 危险操作(清空画布)有确认提示

技术实现

  • 文件:web/src/components/Workspace.tsx
  • React Flow 事件:onEdgeContextMenu, onNodeContextMenu, onPaneContextMenu
  • 监听拖拽开始和鼠标按下事件自动关闭菜单

使用示例

// 删除同一接口的所有连线(用于清理多输入节点)
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
    • 支持常见图片格式

数据结构

interface NodeData {
  preview?: {
    type: 'table' | 'image'
    data: any  // 表格数组或图片URL
    columns?: string[]  // 表格列名
  }
}

表格预览示例

{
  "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" }
    ]
  }
}

图表预览示例

{
  "preview": {
    "type": "image",
    "data": "data:image/png;base64,iVBORw0KG..."
  }
}

技术实现

样式特点

  • 🎨 深色主题适配
  • 📏 固定宽度防止节点过宽
  • 🔤 文本超出自动省略
  • 📊 数据行数统计标签

🛠️ 技术细节

树状分类算法

核心函数

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
}

递归渲染

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>
  )
}

右键菜单状态管理

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 列(避免节点过宽)
  • 单元格内容超出自动省略

响应式设计

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)

"MyNode": {
    "function": "Transform",  # 节点功能
    "category": "Transform/Filter/Advanced/Custom",  # 4级分类
    # 显示效果:
    # Transform > Filter > Advanced (树结构)
    # + "Custom" 标签 (subCategory)
}

步骤 2启动服务

# 后端
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执行节点后自动保存

// 执行完成后更新节点数据
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预览按钮触发

// 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多层级分类

# 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

🔧 配置选项

树层级限制

// web/src/components/NodePalette.tsx
const MAX_TREE_DEPTH = 3  // 修改此值调整最大层级

预览数据限制

// web/src/components/nodes/UniversalNode.tsx
maxHeight: 300,  // 预览区域最大高度
data.slice(0, 5)  // 显示前5行
columns.slice(0, 4)  // 显示前4列

右键菜单样式

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