diff --git a/app/teknav-diagrams.jsx b/app/teknav-diagrams.jsx new file mode 100644 index 0000000..9d149d6 --- /dev/null +++ b/app/teknav-diagrams.jsx @@ -0,0 +1,1181 @@ +import { useState, useEffect, useRef } from 'react'; +import * as THREE from 'three'; +function NeuralNetworkDiagram({ width = 420, height = 280 }) { + const [hovered, setHovered] = useState(null); + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 80); return () => clearInterval(id); }, []); + + const layers = [ + { x: 60, nodes: [80, 130, 180, 230], label: 'لایه ورودی' }, + { x: 160, nodes: [100, 155, 210], label: 'لایه پنهان ۱' }, + { x: 260, nodes: [110, 165, 220], label: 'لایه توجه' }, + { x: 360, nodes: [140, 195], label: 'خروجی مدل' }, + ]; + + const pulses = []; + for (let li = 0; li < layers.length - 1; li++) { + const from = layers[li], to = layers[li + 1]; + from.nodes.forEach((fy, fi) => { + to.nodes.forEach((ty, ti) => { + const phase = (tick + li * 7 + fi * 3 + ti * 5) % 40; + if (phase < 20) pulses.push({ x: from.x + (to.x - from.x) * phase / 20, y: fy + (ty - fy) * phase / 20 }); + }); + }); + } + + const nodeColors = ['#0F6B73', '#D49A2A', '#C76D4A', '#2F8F6B']; + + return ( + + ); +} +function DataPipelineDiagram({ compact = false }) { + const [tick, setTick] = useState(0); + const [hovered, setHovered] = useState(null); + useEffect(() => { const id = setInterval(() => setTick(t => (t + 1) % 100), 60); return () => clearInterval(id); }, []); + + const steps = [ + { id: 'raw', label: 'داده خام', icon: '⬡', color: '#5F6B6D', desc: 'جمعآوری از منابع مختلف' }, + { id: 'clean', label: 'پاکسازی', icon: '◈', color: '#D49A2A', desc: 'حذف تکراری و نویز' }, + { id: 'label', label: 'برچسبگذاری', icon: '◉', color: '#C76D4A', desc: 'دستهبندی و تعریف' }, + { id: 'feature', label: 'ویژگیسازی', icon: '⬟', color: '#0F6B73', desc: 'استخراج ویژگی' }, + { id: 'train', label: 'آموزش مدل', icon: '◈', color: '#2F8F6B', desc: 'یادگیری از داده' }, + { id: 'eval', label: 'ارزیابی', icon: '◉', color: '#D49A2A', desc: 'سنجش عملکرد' }, + { id: 'output', label: 'خروجی', icon: '★', color: '#0F6B73', desc: 'مدل آماده' }, + ]; + + const W = compact ? 480 : 700, H = compact ? 140 : 180; + const stepW = W / steps.length; + + return ( + + ); +} +function CyberSecurityDiagram() { + const [hovered, setHovered] = useState(null); + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 100); return () => clearInterval(id); }, []); + + const cx = 200, cy = 150, r = 90; + const threats = [ + { angle: 0, label: 'فیشینگ', color: '#C94C4C' }, + { angle: 60, label: 'حمله زنجیره تأمین', color: '#D08A22' }, + { angle: 120, label: 'آسیبپذیری کتابخانه', color: '#C76D4A' }, + { angle: 180, label: 'دسترسی غیرمجاز', color: '#C94C4C' }, + { angle: 240, label: 'باجافزار', color: '#D08A22' }, + { angle: 300, label: 'حمله DDoS', color: '#C76D4A' }, + ]; + + return ( + + ); +} +function StartupGrowthDiagram() { + const [progress, setProgress] = useState(0); + useEffect(() => { + let v = 0; + const id = setInterval(() => { v = Math.min(v + 2, 100); setProgress(v); if (v >= 100) clearInterval(id); }, 20); + return () => clearInterval(id); + }, []); + + const milestones = ['ایده', 'محصول اولیه', 'جذب کاربر', 'درآمد', 'مقیاسپذیری']; + const W = 400, H = 200, padX = 40, padY = 20; + const points = milestones.map((_, i) => { + const t = i / (milestones.length - 1); + return { x: padX + t * (W - padX * 2), y: H - padY - Math.pow(t, 1.8) * (H - padY * 3) }; + }); + + const drawProgress = Math.min(progress / 100, 1); + const pathPoints = points.filter((_, i) => i / (milestones.length - 1) <= drawProgress + 0.01); + + const pathD = pathPoints.length > 1 + ? pathPoints.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ') + : ''; + + return ( + + ); +} +function HardwareChipDiagram() { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 70); return () => clearInterval(id); }, []); + + const blocks = [ + { x: 30, y: 60, w: 80, h: 40, label: 'حافظه', color: '#D49A2A' }, + { x: 160, y: 40, w: 80, h: 80, label: 'پردازنده', color: '#0F6B73' }, + { x: 290, y: 60, w: 80, h: 40, label: 'شتابدهنده AI', color: '#C76D4A' }, + { x: 30, y: 150, w: 80, h: 40, label: 'گذرگاه داده', color: '#2F8F6B' }, + { x: 290, y: 150, w: 80, h: 40, label: 'کنترلر حافظه', color: '#5F6B6D' }, + ]; + + const routes = [ + { x1: 110, y1: 80, x2: 160, y2: 80 }, + { x1: 240, y1: 80, x2: 290, y2: 80 }, + { x1: 110, y1: 170, x2: 160, y2: 120 }, + { x1: 240, y1: 100, x2: 290, y2: 170 }, + { x1: 200, y1: 120, x2: 200, y2: 170 }, + ]; + + return ( + + ); +} +function FutureTimelineDiagram() { + const [activeIdx, setActiveIdx] = useState(null); + const milestones = [ + { year: '۲۰۲۵', label: 'AGI اولیه', color: '#0F6B73' }, + { year: '۲۰۲۷', label: 'رایانش کوانتومی', color: '#D49A2A' }, + { year: '۲۰۲۹', label: 'رباتیک خودکار', color: '#C76D4A' }, + { year: '۲۰۳۱', label: 'رابط مغز-ماشین', color: '#2F8F6B' }, + { year: '۲۰۳۵', label: 'جوش هستهای تجاری', color: '#0F6B73' }, + ]; + const W = 480, H = 120; + return ( + + ); +} +function SoftwareArchDiagram() { + const modules = [ + { x: 180, y: 30, label: 'API Gateway', color: '#0F6B73' }, + { x: 60, y: 110, label: 'Auth Service', color: '#2F8F6B' }, + { x: 180, y: 110, label: 'Core Service', color: '#D49A2A' }, + { x: 300, y: 110, label: 'AI Service', color: '#C76D4A' }, + { x: 60, y: 190, label: 'Database', color: '#5F6B6D' }, + { x: 300, y: 190, label: 'Cache', color: '#5F6B6D' }, + ]; + const edges = [[0,1],[0,2],[0,3],[1,4],[2,4],[3,5]]; + return ( + + ); +} +function WaveStreamDiagram() { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 40); return () => clearInterval(id); }, []); + const W = 480, H = 160; + const streams = [ + { color: '#D49A2A', amp: 28, freq: 0.022, phase: 0, y: 50 }, + { color: '#C46A4D', amp: 20, freq: 0.03, phase: 1.5, y: 85 }, + { color: '#2F8F6B', amp: 24, freq: 0.018, phase: 3, y: 120 }, + ]; + const buildPath = (s) => { + const pts = Array.from({ length: 60 }, (_, i) => { + const x = (i / 59) * W; + const y = s.y + s.amp * Math.sin(x * s.freq + tick * 0.08 + s.phase); + return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)},${y.toFixed(1)}`; + }); + return pts.join(' '); + }; + return ( + + ); +} + +function OrbitDiagram() { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 50); return () => clearInterval(id); }, []); + const cx = 140, cy = 110; + const orbits = [ + { r: 40, speed: 0.04, color: '#D49A2A', label: 'داده', dotR: 7 }, + { r: 65, speed: -0.025, color: '#C46A4D', label: 'مدل', dotR: 9 }, + { r: 90, speed: 0.016, color: '#2F8F6B', label: 'استنتاج', dotR: 8 }, + ]; + return ( + + ); +} +function GeneratedDiagram({ variant = 1, compact = false }) { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick((t) => t + 1), 50); return () => clearInterval(id); }, []); + const W = compact ? 320 : 520; + const H = compact ? 150 : 210; + const seed = Number.isFinite(variant) ? variant : 1; + const nodeCount = 6 + (seed % 7); + const radius = compact ? 36 + (seed % 5) * 6 : 48 + (seed % 6) * 7; + const centerX = W * 0.5; + const centerY = H * 0.48; + const palette = ['#0F6B73', '#D49A2A', '#C76D4A', '#2F8F6B', '#5F6B6D']; + const points = Array.from({ length: nodeCount }, (_, i) => { + const angle = ((Math.PI * 2) / nodeCount) * i + (seed % 9) * 0.11; + return { x: centerX + Math.cos(angle) * radius, y: centerY + Math.sin(angle) * radius, color: palette[(i + seed) % palette.length] }; + }); + const pulse = (tick + seed * 3) % 100; + return ( + + ); +} +function BrainDiagram({ compact = false }) { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 60); return () => clearInterval(id); }, []); + const W = compact ? 300 : 400, H = compact ? 180 : 260; + const cx = W / 2, cy = H * 0.45; + const nodes = [ + { x: cx, y: cy - 60 }, { x: cx - 50, y: cy - 30 }, { x: cx + 50, y: cy - 30 }, + { x: cx - 70, y: cy + 20 }, { x: cx + 70, y: cy + 20 }, { x: cx, y: cy + 60 }, + { x: cx - 30, y: cy + 40 }, { x: cx + 30, y: cy + 40 } + ]; + return ( + + ); +} +function DatabaseDiagram({ compact = false }) { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 50); return () => clearInterval(id); }, []); + const W = 320, H = 200; + const stacks = [ + { x: 60, y: 140, h: 40, color: '#5F6B6D' }, + { x: 160, y: 140, h: 60, color: '#D49A2A' }, + { x: 260, y: 140, h: 50, color: '#0F6B73' }, + ]; + return ( + + ); +} +function CloudDiagram({ compact = false }) { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 80); return () => clearInterval(id); }, []); + return ( + + ); +} +function QuantumDiagram({ compact = false }) { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 40); return () => clearInterval(id); }, []); + const cx1 = 120, cy = 100, cx2 = 280; + const orbit = tick * 0.05; + return ( + + ); +} +function VlaDiagram({ compact = false }) { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 50); return () => clearInterval(id); }, []); + return ( + + ); +} +function DnaDiagram({ compact = false }) { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 60); return () => clearInterval(id); }, []); + const W = 320, H = 200; + const nodes = Array.from({ length: 10 }, (_, i) => i); + return ( + + ); +} +function FusionDiagram({ compact = false }) { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 40); return () => clearInterval(id); }, []); + const cx = 160, cy = 100; + return ( + + ); +} +function SatelliteDiagram({ compact = false }) { + const [tick, setTick] = useState(0); + useEffect(() => { const id = setInterval(() => setTick(t => t + 1), 50); return () => clearInterval(id); }, []); + const cx = 160, cy = 100; + const satX = cx + Math.cos(tick * 0.03) * 80; + const satY = cy + Math.sin(tick * 0.03) * 40; + return ( + + ); +} +function BlockchainDiagram({ compact = false }) { + const blocks = [0, 1, 2, 3]; + return ( + + ); +} +function TekRagThreeDiagram({ variant = 'corpus', compact = false }) { + const hostRef = useRef(null); + const [hoverLabel, setHoverLabel] = useState('TekRAG-Persian'); + + useEffect(() => { + const host = hostRef.current; + if (!host) return undefined; + + const scene = new THREE.Scene(); + const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100); + camera.position.set(0, 3.2, 8.5); + + const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); + renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); + renderer.outputColorSpace = THREE.SRGBColorSpace; + host.appendChild(renderer.domElement); + + const group = new THREE.Group(); + scene.add(group); + + scene.add(new THREE.AmbientLight(0xffffff, 0.7)); + const key = new THREE.DirectionalLight(0xffffff, 1.4); + key.position.set(4, 6, 5); + scene.add(key); + const rim = new THREE.DirectionalLight(0xd49a2a, 0.8); + rim.position.set(-5, 3, -4); + scene.add(rim); + + const pickables = []; + const palette = { + teal: 0x0f6b73, + gold: 0xd49a2a, + clay: 0xc76d4a, + green: 0x2f8f6b, + ink: 0x20343a, + line: 0xe4ddd2, + red: 0xc94c4c, + cream: 0xf4efe6, + }; + + const addLabel = (object, label) => { + object.userData.label = label; + pickables.push(object); + return object; + }; + + const box = (label, position, scale, color, opacity = 0.92) => { + const mesh = new THREE.Mesh( + new THREE.BoxGeometry(scale[0], scale[1], scale[2]), + new THREE.MeshStandardMaterial({ color, roughness: 0.48, metalness: 0.12, transparent: opacity < 1, opacity }) + ); + mesh.position.set(...position); + group.add(addLabel(mesh, label)); + const edges = new THREE.LineSegments( + new THREE.EdgesGeometry(mesh.geometry), + new THREE.LineBasicMaterial({ color: 0xffffff, transparent: true, opacity: 0.18 }) + ); + mesh.add(edges); + return mesh; + }; + + const sphere = (label, position, radius, color, opacity = 0.95) => { + const mesh = new THREE.Mesh( + new THREE.SphereGeometry(radius, 28, 18), + new THREE.MeshStandardMaterial({ color, roughness: 0.35, metalness: 0.2, transparent: opacity < 1, opacity }) + ); + mesh.position.set(...position); + group.add(addLabel(mesh, label)); + return mesh; + }; + + const line = (from, to, color = palette.line, opacity = 0.45) => { + const geometry = new THREE.BufferGeometry().setFromPoints([ + new THREE.Vector3(...from), + new THREE.Vector3(...to), + ]); + const mesh = new THREE.Line(geometry, new THREE.LineBasicMaterial({ color, transparent: true, opacity })); + group.add(mesh); + return mesh; + }; + + if (variant === 'corpus') { + const layers = [ + ['Raw HTML export', -1.8, palette.ink], + ['Persian normalization', -0.6, palette.teal], + ['Heading-aware chunks', 0.6, palette.gold], + ['Metadata and provenance', 1.8, palette.green], + ]; + layers.forEach(([label, y, color], idx) => { + const slab = box(label, [0, y, 0], [5.8, 0.22, 3.2], color, 0.42); + slab.rotation.y = idx % 2 ? 0.08 : -0.08; + for (let i = 0; i < 9; i += 1) { + const x = -2.2 + (i % 3) * 2.2 + (idx % 2 ? 0.15 : -0.1); + const z = -1.05 + Math.floor(i / 3) * 1.05; + const doc = sphere(`${label}: document ${i + 1}`, [x, y + 0.28, z], 0.09, idx % 2 ? palette.cream : palette.gold, 0.82); + doc.scale.y = 0.55; + } + }); + for (let i = 0; i < 8; i += 1) { + const x = -2.3 + (i % 4) * 1.55; + const z = -1.35 + (i % 2) * 2.7; + line([x, -1.65, z], [x * 0.72, 1.95, z * 0.55], palette.gold, 0.36); + } + sphere('Benchmark release: QA plus evidence IDs', [0, 2.45, 0], 0.38, palette.clay); + camera.position.set(0, 4.1, 8.2); + } else if (variant === 'retrieval') { + line([-3.3, 0, 0], [3.3, 0, 0], palette.teal, 0.5); + line([0, -2.2, 0], [0, 2.2, 0], palette.gold, 0.5); + line([0, 0, -2.8], [0, 0, 2.8], palette.green, 0.5); + const clusters = [ + ['BM25 exact acronym region', [-1.75, 0.45, -0.6], palette.gold], + ['Dense semantic Persian region', [1.55, 0.1, 0.75], palette.teal], + ['Hybrid fused evidence region', [0.2, 1.2, -0.1], palette.green], + ]; + clusters.forEach(([label, center, color], ci) => { + sphere(label, center, 0.3, color, 0.82); + for (let i = 0; i < 18; i += 1) { + const a = (i / 18) * Math.PI * 2; + const r = 0.35 + (i % 5) * 0.09; + const p = [ + center[0] + Math.cos(a) * r, + center[1] + Math.sin(a * 1.7) * 0.38, + center[2] + Math.sin(a) * r, + ]; + sphere(`${label}: chunk ${i + 1}`, p, 0.055, color, 0.76); + } + }); + const query = sphere('Persian technical user query', [-3.0, -1.45, 1.7], 0.22, palette.red); + clusters.forEach(([, center], i) => line(query.position.toArray(), center, i === 1 ? palette.teal : palette.gold, 0.48)); + box('Reranker: top-k evidence gate', [0, -1.55, 0], [1.9, 0.16, 1.0], palette.ink, 0.68); + camera.position.set(0.2, 2.9, 7.4); + } else { + const metrics = [ + ['Recall@k', -2.4, 1.55, palette.teal], + ['Citation precision', -1.2, 2.1, palette.green], + ['Atomic support precision', 0, 2.45, palette.gold], + ['Abstention F1', 1.2, 1.65, palette.clay], + ['Unsupported-claim rate', 2.4, 0.9, palette.red], + ]; + metrics.forEach(([label, x, h, color]) => { + const pillar = box(label, [x, h / 2 - 1.2, 0], [0.52, h, 0.52], color, 0.9); + const cap = sphere(`${label}: confidence interval marker`, [x, h - 1.0, 0], 0.18, color, 0.95); + line([x, h - 1.0, -0.65], [x, h - 1.0, 0.65], color, 0.48); + pillar.rotation.y = 0.2; + cap.scale.y = 0.7; + }); + const ringGeo = new THREE.TorusGeometry(1.55, 0.025, 12, 96); + const ring = new THREE.Mesh(ringGeo, new THREE.MeshStandardMaterial({ color: palette.teal, transparent: true, opacity: 0.5 })); + ring.rotation.x = Math.PI / 2; + ring.position.y = -1.05; + group.add(addLabel(ring, 'Paired statistical testing and confidence intervals')); + for (let i = 0; i < 14; i += 1) { + const a = (i / 14) * Math.PI * 2; + sphere('Hallucination error atom', [Math.cos(a) * 1.55, -1.05, Math.sin(a) * 1.55], 0.065, i % 3 === 0 ? palette.red : palette.gold, 0.82); + } + camera.position.set(0, 3.0, 7.2); + } + + const raycaster = new THREE.Raycaster(); + const pointer = new THREE.Vector2(); + let raf = 0; + let dragging = false; + let lastX = 0; + let lastY = 0; + let targetX = -0.16; + let targetY = 0.34; + + renderer.domElement.style.display = 'block'; + const resize = () => { + const rect = host.getBoundingClientRect(); + const width = Math.max(rect.width || host.offsetWidth || 600, 280); + const height = compact ? 240 : 340; + renderer.setSize(width, height); + camera.aspect = width / height; + camera.updateProjectionMatrix(); + }; + resize(); + requestAnimationFrame(resize); + const resizeObserver = new ResizeObserver(resize); + resizeObserver.observe(host); + + const updatePointer = (event) => { + const rect = renderer.domElement.getBoundingClientRect(); + pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; + pointer.y = -((event.clientY - rect.top) / rect.height) * 2 + 1; + raycaster.setFromCamera(pointer, camera); + const hit = raycaster.intersectObjects(pickables, true)[0]; + const label = hit?.object?.userData?.label || hit?.object?.parent?.userData?.label; + if (label) setHoverLabel(label); + if (dragging) { + targetY += (event.clientX - lastX) * 0.008; + targetX += (event.clientY - lastY) * 0.006; + lastX = event.clientX; + lastY = event.clientY; + } + }; + const pointerDown = (event) => { + dragging = true; + lastX = event.clientX; + lastY = event.clientY; + renderer.domElement.setPointerCapture?.(event.pointerId); + }; + const pointerUp = (event) => { + dragging = false; + renderer.domElement.releasePointerCapture?.(event.pointerId); + }; + + renderer.domElement.addEventListener('pointermove', updatePointer); + renderer.domElement.addEventListener('pointerdown', pointerDown); + renderer.domElement.addEventListener('pointerup', pointerUp); + renderer.domElement.addEventListener('pointerleave', pointerUp); + + const animate = () => { + targetY += dragging ? 0 : 0.003; + group.rotation.x += (targetX - group.rotation.x) * 0.08; + group.rotation.y += (targetY - group.rotation.y) * 0.08; + renderer.render(scene, camera); + raf = requestAnimationFrame(animate); + }; + animate(); + + return () => { + cancelAnimationFrame(raf); + resizeObserver.disconnect(); + renderer.domElement.removeEventListener('pointermove', updatePointer); + renderer.domElement.removeEventListener('pointerdown', pointerDown); + renderer.domElement.removeEventListener('pointerup', pointerUp); + renderer.domElement.removeEventListener('pointerleave', pointerUp); + renderer.dispose(); + scene.traverse((object) => { + object.geometry?.dispose?.(); + if (Array.isArray(object.material)) object.material.forEach((m) => m.dispose?.()); + else object.material?.dispose?.(); + }); + host.replaceChildren(); + }; + }, [variant, compact]); + + const labels = { + corpus: ['Raw', 'Cleaned', 'Metadata', 'Provenance'], + retrieval: ['BM25', 'Dense', 'Hybrid', 'Rerank'], + evaluation: ['Recall', 'Citation', 'Support', 'Abstention'], + }[variant] || ['TekRAG', 'RAG', 'QA']; + + return ( +