refactor(classroom): unify whiteboard rendering to WhiteboardCanvas
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

Replace inline SVG whiteboard rendering in SceneRenderer with the
dedicated WhiteboardCanvas component, gaining chart/latex support
and eliminating 27 lines of duplicated rendering logic.
This commit is contained in:
iven
2026-04-06 10:53:21 +08:00
parent 828be3cc9e
commit 38e7c7bd9b
2 changed files with 13 additions and 39 deletions

View File

@@ -7,6 +7,7 @@
import { useState, useEffect, useCallback } from 'react'; import { useState, useEffect, useCallback } from 'react';
import type { GeneratedScene, SceneContent, SceneAction, AgentProfile } from '../../types/classroom'; import type { GeneratedScene, SceneContent, SceneAction, AgentProfile } from '../../types/classroom';
import { WhiteboardCanvas } from './WhiteboardCanvas';
interface SceneRendererProps { interface SceneRendererProps {
scene: GeneratedScene; scene: GeneratedScene;
@@ -79,12 +80,8 @@ export function SceneRenderer({ scene, agents, autoPlay = true }: SceneRendererP
{/* Whiteboard area */} {/* Whiteboard area */}
{whiteboardItems.length > 0 && ( {whiteboardItems.length > 0 && (
<div className="w-80 border border-gray-200 dark:border-gray-700 rounded-lg bg-white dark:bg-gray-800 p-2 overflow-auto"> <div className="w-96 shrink-0">
<svg viewBox="0 0 800 600" className="w-full h-full"> <WhiteboardCanvas items={whiteboardItems} />
{whiteboardItems.map((item, i) => (
<g key={i}>{renderWhiteboardItem(item)}</g>
))}
</svg>
</div> </div>
)} )}
</div> </div>
@@ -127,6 +124,9 @@ function getActionDelay(action: SceneAction): number {
case 'speech': return 2000; case 'speech': return 2000;
case 'whiteboard_draw_text': return 800; case 'whiteboard_draw_text': return 800;
case 'whiteboard_draw_shape': return 600; case 'whiteboard_draw_shape': return 600;
case 'whiteboard_draw_chart': return 1000;
case 'whiteboard_draw_latex': return 1000;
case 'whiteboard_clear': return 300;
case 'quiz_show': return 5000; case 'quiz_show': return 5000;
case 'discussion': return 10000; case 'discussion': return 10000;
default: return 1000; default: return 1000;
@@ -190,30 +190,3 @@ function renderCurrentAction(action: SceneAction, agents: AgentProfile[]) {
} }
} }
function renderWhiteboardItem(item: { type: string; data: Record<string, unknown> }) {
switch (item.type) {
case 'whiteboard_draw_text': {
const d = item.data;
if ('text' in d && 'x' in d && 'y' in d) {
return (
<text x={typeof d.x === 'number' ? d.x : 100} y={typeof d.y === 'number' ? d.y : 100} fontSize={typeof d.fontSize === 'number' ? d.fontSize : 16} fill={typeof d.color === 'string' ? d.color : '#333'}>
{String(d.text ?? '')}
</text>
);
}
return null;
}
case 'whiteboard_draw_shape': {
const d = item.data as Record<string, unknown>;
const x = typeof d.x === 'number' ? d.x : 0;
const y = typeof d.y === 'number' ? d.y : 0;
const w = typeof d.width === 'number' ? d.width : 100;
const h = typeof d.height === 'number' ? d.height : 50;
const fill = typeof d.fill === 'string' ? d.fill : '#e5e5e5';
return (
<rect x={x} y={y} width={w} height={h} fill={fill} />
);
}
}
}

View File

@@ -1,6 +1,6 @@
# P3-02: 白板统一渲染方案 # P3-02: 白板统一渲染方案
> **状态**: 方案已制定,待新会话推进实现 > **状态**: ✅ 已完成 (2026-04-06)
> **优先级**: P3 (非阻塞) > **优先级**: P3 (非阻塞)
> **依赖**: ClassroomPlayer 重构 > **依赖**: ClassroomPlayer 重构
@@ -50,11 +50,12 @@ SceneRenderer 的 `processAction()` 产出的 `{ type, data: SceneAction }` 格
#### Step 4: 验证 #### Step 4: 验证
- [ ] ClassroomPlayer 中白板绘制正常text/shape - [x] ClassroomPlayer 中白板绘制正常text/shape
- [ ] Chart 渲染正常bar/line - [x] Chart 渲染正常bar/line
- [ ] LaTeX 渲染正常 - [x] LaTeX 渲染正常
- [ ] 自动推进动作序列正常 - [x] 自动推进动作序列正常
- [ ] 白板清空 (`whiteboard_clear`) 正常 - [x] 白板清空 (`whiteboard_clear`) 正常
- [x] TypeScript 类型检查通过
## 3. 影响范围 ## 3. 影响范围