Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | import { ImageResponse } from 'next/og' import { readFileSync } from 'fs' import { join } from 'path' // Route segment config export const runtime = 'nodejs' export const dynamic = 'force-dynamic' // Image metadata export const alt = 'Abaci.One - Interactive Soroban Learning Platform' export const size = { width: 1200, height: 630, } export const contentType = 'image/png' // Extract just the abacus SVG content from the pre-generated og-image.svg // This SVG is generated by scripts/generateAbacusIcons.tsx using AbacusReact function getAbacusSVGContent(): string { const svgPath = join(process.cwd(), 'public', 'og-image.svg') const svgContent = readFileSync(svgPath, 'utf-8') // Extract just the abacus <g> element (contains the AbacusReact output) const abacusMatch = svgContent.match( /<!-- Left side - Abacus from @soroban\/abacus-react -->\s*<g[^>]*>([\s\S]*?)<\/g>/ ) if (!abacusMatch) { throw new Error('Could not extract abacus content from og-image.svg') } return abacusMatch[0] // Return the full <g>...</g> block with AbacusReact output } // Image generation // Note: Uses pre-generated SVG from og-image.svg which is rendered by AbacusReact // This avoids importing react-dom/server in this file (Next.js restriction) export default async function Image() { const abacusSVG = getAbacusSVGContent() return new ImageResponse( <div style={{ background: 'linear-gradient(135deg, #fef3c7 0%, #fcd34d 100%)', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '80px', }} > {/* Left side - Abacus from pre-generated og-image.svg (AbacusReact output) */} <div style={{ display: 'flex', width: '40%', }} dangerouslySetInnerHTML={{ __html: abacusSVG, }} /> {/* Right side - Text content */} <div style={{ display: 'flex', flexDirection: 'column', width: '55%', gap: '30px', }} > <h1 style={{ fontSize: '80px', fontWeight: 'bold', color: '#7c2d12', margin: 0, lineHeight: 1, }} > Abaci.One </h1> <p style={{ fontSize: '40px', fontWeight: 600, color: '#92400e', margin: 0, lineHeight: 1.2, }} > Learn Soroban Through Play </p> <div style={{ display: 'flex', flexDirection: 'column', gap: '15px', fontSize: '32px', color: '#78350f', }} > <div style={{ display: 'flex', alignItems: 'center' }}>• Interactive Games</div> <div style={{ display: 'flex', alignItems: 'center' }}>• Tutorials</div> <div style={{ display: 'flex', alignItems: 'center' }}>• Practice Tools</div> </div> </div> </div>, { ...size, } ) } |