import { lazy, Suspense, useEffect, useMemo, useState } from 'react'; import CurrentProofCard from './CurrentProofCard'; import ThreeFallback from './ThreeD/ThreeFallback'; import { prefer3DByDefault, useWebGLSupport } from './ThreeD/useWebGL'; import type { SimulationState } from '../domain/types'; const SorterDigitalTwin = lazy(() => import('./ThreeD/SorterDigitalTwin')); type ViewMode = '3d' | '2d'; interface Props { simulation: SimulationState; demoStepTitle: string; onStartDemo: () => void; onNext: () => void; onReset: () => void; onOpenEngineering: () => void; } export default function ProductDemoSection({ simulation, demoStepTitle, onStartDemo, onNext, onReset, onOpenEngineering, }: Props) { const webgl = useWebGLSupport(); const [width, setWidth] = useState(() => (typeof window === 'undefined' ? 1200 : window.innerWidth)); const [viewMode, setViewMode] = useState('3d'); const [contextLost, setContextLost] = useState(false); useEffect(() => { const onResize = () => setWidth(window.innerWidth); window.addEventListener('resize', onResize); return () => window.removeEventListener('resize', onResize); }, []); useEffect(() => { setViewMode(prefer3DByDefault(width, webgl && !contextLost) ? '3d' : '2d'); }, [webgl, width, contextLost]); const item = simulation.currentItem; const category = item?.classification.category; const command = simulation.machineState.startsWith('ROUTE_TO_') ? simulation.machineState : item?.classification ? `ROUTE_TO_${item.classification.category}` : '—'; const fallbackReason = useMemo(() => { if (!webgl || contextLost) return 'webgl' as const; if (width < 640) return 'mobile' as const; return 'user' as const; }, [webgl, contextLost, width]); const show3D = viewMode === '3d' && webgl && !contextLost; const simplified = width < 900; return (

Live product demo

Главное демо

3D digital twin показывает физическую маршрутизацию: A → конвейер → CV → накопитель → gate → B/C/D. Справа — товар, категория, причина и команда ROUTE_TO_*.

Шаг: {demoStepTitle}

Товар проходит камеру, накопитель и gate, затем уходит в зону B, C или D.

{show3D ? ( Загрузка 3D digital twin…
}> { setContextLost(true); setViewMode('2d'); }} /> ) : ( )}
); }