|
| 1 | +# Design Document: Canvas-Centric UI |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +本设计文档描述如何优化二叉树算法可视化界面,将信息呈现从"文字说明为主"转变为"画布可视化为主"。核心目标是减少界面上的文字,让用户通过视觉元素直观理解算法执行过程。 |
| 6 | + |
| 7 | +## Architecture |
| 8 | + |
| 9 | +### 当前架构问题 |
| 10 | + |
| 11 | +``` |
| 12 | +当前布局: |
| 13 | +┌─────────────────────────────────────────────────┐ |
| 14 | +│ 文字说明区域 (占用空间) │ |
| 15 | +├─────────────────────────────────────────────────┤ |
| 16 | +│ │ |
| 17 | +│ 画布区域 (被压缩) │ |
| 18 | +│ │ |
| 19 | +├─────────────────────────────────────────────────┤ |
| 20 | +│ 步骤说明 + 递归可视化 (大量文字) │ |
| 21 | +└─────────────────────────────────────────────────┘ |
| 22 | +``` |
| 23 | + |
| 24 | +### 目标架构 |
| 25 | + |
| 26 | +``` |
| 27 | +新布局: |
| 28 | +┌─────────────────────────────────────────────────┐ |
| 29 | +│ │ |
| 30 | +│ 画布区域 (最大化, ≥70% 高度) │ |
| 31 | +│ ┌─────────┐ │ |
| 32 | +│ │递归栈 │ [节点上的内联标注] │ |
| 33 | +│ │(紧凑) │ │ |
| 34 | +│ └─────────┘ │ |
| 35 | +│ │ |
| 36 | +├─────────────────────────────────────────────────┤ |
| 37 | +│ [简化的底部控制栏 - 仅图标和进度] │ |
| 38 | +└─────────────────────────────────────────────────┘ |
| 39 | +``` |
| 40 | + |
| 41 | +### 修改范围 |
| 42 | + |
| 43 | +需要修改的核心文件: |
| 44 | +1. `TreeVisualizationNew.tsx` - 添加内联标注功能 |
| 45 | +2. `RecursionCallVisualizer.tsx` - 重构为紧凑的画布内组件 |
| 46 | +3. `TreeVisualExplanations.tsx` - 简化为图标+短文本 |
| 47 | +4. `SymmetricTreeCheck.tsx` - 调整布局比例 |
| 48 | +5. 新增 `InlineAnnotation.tsx` - 画布内标注组件 |
| 49 | +6. 新增 `CompactRecursionStack.tsx` - 紧凑递归栈组件 |
| 50 | + |
| 51 | +## Components and Interfaces |
| 52 | + |
| 53 | +### 1. 内联标注组件 |
| 54 | + |
| 55 | +```typescript |
| 56 | +interface InlineAnnotationProps { |
| 57 | + nodeId: string; |
| 58 | + text: string; // 最多15字符 |
| 59 | + type: 'compare' | 'result' | 'info'; |
| 60 | + position: 'top' | 'bottom' | 'left' | 'right'; |
| 61 | +} |
| 62 | + |
| 63 | +// 在节点附近显示简短标注 |
| 64 | +function InlineAnnotation(props: InlineAnnotationProps): JSX.Element; |
| 65 | +``` |
| 66 | + |
| 67 | +### 2. 紧凑递归栈组件 |
| 68 | + |
| 69 | +```typescript |
| 70 | +interface CompactRecursionStackProps { |
| 71 | + frames: RecursionFrame[]; |
| 72 | + activeFrameIndex: number; |
| 73 | + onFrameClick?: (index: number) => void; |
| 74 | +} |
| 75 | + |
| 76 | +interface RecursionFrame { |
| 77 | + id: string; |
| 78 | + leftNodeId: string | null; |
| 79 | + rightNodeId: string | null; |
| 80 | + depth: number; |
| 81 | + status: 'active' | 'completed' | 'pending'; |
| 82 | + result?: boolean; |
| 83 | +} |
| 84 | + |
| 85 | +// 紧凑的递归栈显示,作为画布内的浮动面板 |
| 86 | +function CompactRecursionStack(props: CompactRecursionStackProps): JSX.Element; |
| 87 | +``` |
| 88 | + |
| 89 | +### 3. 视觉比较指示器 |
| 90 | + |
| 91 | +```typescript |
| 92 | +interface ComparisonIndicatorProps { |
| 93 | + leftNodeId: string; |
| 94 | + rightNodeId: string; |
| 95 | + result?: boolean; // undefined = 进行中, true = 相等, false = 不等 |
| 96 | +} |
| 97 | + |
| 98 | +// 在两个节点之间显示比较状态的视觉指示 |
| 99 | +function createComparisonIndicator( |
| 100 | + svg: d3.Selection<SVGGElement>, |
| 101 | + props: ComparisonIndicatorProps, |
| 102 | + positions: Map<string, NodePosition> |
| 103 | +): void; |
| 104 | +``` |
| 105 | + |
| 106 | +### 4. 结果图标 |
| 107 | + |
| 108 | +```typescript |
| 109 | +type ResultIconType = 'check' | 'cross' | 'comparing'; |
| 110 | + |
| 111 | +// 在节点上显示结果图标 |
| 112 | +function addResultIcon( |
| 113 | + nodeGroup: d3.Selection<SVGGElement>, |
| 114 | + type: ResultIconType |
| 115 | +): void; |
| 116 | +``` |
| 117 | + |
| 118 | +## Data Models |
| 119 | + |
| 120 | +### AnnotationData |
| 121 | + |
| 122 | +```typescript |
| 123 | +interface AnnotationData { |
| 124 | + id: string; |
| 125 | + nodeId: string; |
| 126 | + text: string; |
| 127 | + type: 'compare' | 'result' | 'info'; |
| 128 | + visible: boolean; |
| 129 | +} |
| 130 | +``` |
| 131 | + |
| 132 | +### LayoutConfig |
| 133 | + |
| 134 | +```typescript |
| 135 | +interface LayoutConfig { |
| 136 | + canvasHeightPercent: number; // 默认 75 |
| 137 | + recursionStackWidth: number; // 默认 180px |
| 138 | + showTextExplanations: boolean; // 默认 false |
| 139 | + useTooltips: boolean; // 默认 true |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +## Correctness Properties |
| 144 | + |
| 145 | +*A property is a characteristic or behavior that should hold true across all valid executions of a system-essentially, a formal statement about what the system should do. Properties serve as the bridge between human-readable specifications and machine-verifiable correctness guarantees.* |
| 146 | + |
| 147 | +### Property 1: Annotation Brevity |
| 148 | +*For any* inline annotation displayed on the canvas, the text content SHALL NOT exceed 15 characters in length. |
| 149 | +**Validates: Requirements 1.1, 1.3** |
| 150 | + |
| 151 | +### Property 2: Comparison Visual Feedback |
| 152 | +*For any* node comparison operation, the system SHALL display visual indicators (color coding, connection lines, or icons) on both compared nodes, and when a result is determined, SHALL show a checkmark (✓) or cross (✗) icon. |
| 153 | +**Validates: Requirements 1.2, 3.1, 3.2** |
| 154 | + |
| 155 | +### Property 3: Recursion Frame Node Synchronization |
| 156 | +*For any* active recursion frame, the corresponding left and right nodes in the tree visualization SHALL be highlighted with matching visual indicators. |
| 157 | +**Validates: Requirements 2.2** |
| 158 | + |
| 159 | +### Property 4: Canvas Size Preservation |
| 160 | +*For any* text explanation or tooltip displayed, the canvas area dimensions SHALL remain unchanged (no reduction in width or height). |
| 161 | +**Validates: Requirements 4.2** |
| 162 | + |
| 163 | +## Error Handling |
| 164 | + |
| 165 | +1. **标注溢出**: 当标注文本超过15字符时,自动截断并添加省略号 |
| 166 | +2. **节点重叠**: 当多个标注可能重叠时,使用智能定位算法避免遮挡 |
| 167 | +3. **递归栈过深**: 当递归深度超过可视区域时,使用滚动或折叠显示 |
| 168 | +4. **空节点处理**: 空节点的标注使用特殊样式(虚线边框) |
| 169 | + |
| 170 | +## Testing Strategy |
| 171 | + |
| 172 | +### 单元测试 |
| 173 | + |
| 174 | +使用 Vitest 进行单元测试: |
| 175 | + |
| 176 | +1. 测试 `truncateAnnotation` 函数正确截断超长文本 |
| 177 | +2. 测试 `calculateAnnotationPosition` 函数返回正确的位置 |
| 178 | +3. 测试 `CompactRecursionStack` 组件正确渲染帧列表 |
| 179 | + |
| 180 | +### 属性测试 |
| 181 | + |
| 182 | +使用 fast-check 进行属性测试: |
| 183 | + |
| 184 | +1. **Property 1 测试**: 生成随机长度的标注文本,验证显示文本不超过15字符 |
| 185 | +2. **Property 2 测试**: 生成随机节点比较操作,验证视觉指示器正确显示 |
| 186 | +3. **Property 3 测试**: 生成随机递归帧序列,验证节点高亮与帧同步 |
| 187 | +4. **Property 4 测试**: 生成随机tooltip显示操作,验证画布尺寸不变 |
| 188 | + |
| 189 | +每个属性测试配置运行至少 100 次迭代。 |
| 190 | + |
| 191 | +测试文件命名规范:`*.test.ts` |
| 192 | + |
| 193 | +测试标注格式: |
| 194 | +```typescript |
| 195 | +// **Feature: canvas-centric-ui, Property 1: Annotation Brevity** |
| 196 | +``` |
0 commit comments