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
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:
@@ -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} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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. 影响范围
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user