/* global React, Chessboard, FORK_POSITION, FORK_E5_KNIGHT_MOVES, FORK_SOLUTION */ const { useState: useStateL, useEffect: useEffectL, useRef: useRefL } = React; // Responsive sizing: measure container width and cap at maxSize. // We measure a zero-height sentinel that doesn't grow with content, // so the observed width is truly the available column width. function useResponsiveSize(maxSize) { const ref = useRefL(null); const [size, setSize] = useStateL(maxSize); useEffectL(() => { if (!ref.current) return; const update = () => { if (!ref.current) return; const w = ref.current.getBoundingClientRect().width; if (w > 0) setSize(Math.min(maxSize, Math.floor(w))); }; const ro = new ResizeObserver(update); ro.observe(ref.current); update(); window.addEventListener('resize', update); return () => { ro.disconnect(); window.removeEventListener('resize', update); }; }, [maxSize]); return [ref, size]; } // ===================================================================== // InteractiveBoard — landing demo with the Nxf7 fork puzzle. // Click the highlighted white knight, then pick the destination. // Only e5 → f7 wins. // ===================================================================== function InteractiveBoard({ size: maxSize = 440 }) { const [containerRef, size] = useResponsiveSize(maxSize); const [selected, setSelected] = useStateL(null); // 'e5' or null const [status, setStatus] = useStateL('idle'); // idle | wrong-piece | wrong-square | solved function reset() { setSelected(null); setStatus('idle'); } function handleSquareClick(sq) { if (status === 'solved') return; if (!selected) { // selection phase if (sq === 'e5') { setSelected('e5'); setStatus('idle'); } else { setStatus('wrong-piece'); } return; } // move phase if (sq === selected) { setSelected(null); setStatus('idle'); return; } const moves = FORK_E5_KNIGHT_MOVES.map((m) => (m.endsWith('!') ? m.slice(0, -1) : m)); if (!moves.includes(sq)) { // ignore off-target clicks; keep selection return; } if (sq === FORK_SOLUTION.to) { setStatus('solved'); setSelected(null); } else { setStatus('wrong-square'); setSelected(null); } } const candidates = selected === 'e5' ? FORK_E5_KNIGHT_MOVES : []; const hintSet = !selected && status !== 'solved' ? new Set(['e5']) : null; return (
{/* Sentinel: zero-height bar measured to derive the board size — its width tracks the parent column even when the board would otherwise push the column wider than the viewport. */}
{status === 'solved' ? 'Nxf7 \u2014 knight forks queen and rook.' : 'Find the best move'}
{status === 'idle' && !selected && 'White to move. Click the highlighted knight.'} {status === 'idle' && selected && 'Now pick its destination.'} {status === 'wrong-piece' && 'Try the highlighted knight on e5.'} {status === 'wrong-square' && 'Almost. There\u2019s a square that wins material.'} {status === 'solved' && 'Solved. In the app you\u2019d earn +5 minutes.'}
{status === 'solved' && ( )}
); } Object.assign(window, { InteractiveBoard });