import { ImageResponse } from 'next/og'; import { getRegisters } from '@/services/register.service'; export const runtime = 'nodejs'; export const size = { width: 1200, height: 630, }; export const contentType = 'image/png'; const buildRegisterSummaryLines = (register: { description: string; text: string; modes: { text: string }[] }) => { const isInfoLine = (line: string) => line.length > 0 && !line.startsWith('//') && !line.startsWith('(R') && !line.startsWith('(W') && !line.startsWith('(R/W') && !line.startsWith('*') && !/^bits?\s+\d/i.test(line); const normalizeLines = (raw: string) => { const lines: string[] = []; const rawLines = raw.split('\n'); for (const rawLine of rawLines) { const trimmed = rawLine.trim(); if (!trimmed) { if (lines.length > 0 && lines[lines.length - 1] !== '') { lines.push(''); } continue; } if (isInfoLine(trimmed)) { lines.push(trimmed); } } return lines; }; const textLines = normalizeLines(register.text); const modeLines = register.modes.flatMap(mode => normalizeLines(mode.text)); const descriptionLines = normalizeLines(register.description); const combined: string[] = []; const appendBlock = (block: string[]) => { if (block.length === 0) return; if (combined.length > 0 && combined[combined.length - 1] !== '') { combined.push(''); } combined.push(...block); }; appendBlock(textLines); appendBlock(modeLines); appendBlock(descriptionLines); const deduped: string[] = []; const seen = new Set(); for (const line of combined) { if (!line) { if (deduped.length > 0 && deduped[deduped.length - 1] !== '') { deduped.push(''); } continue; } if (seen.has(line)) { continue; } seen.add(line); deduped.push(line); } return deduped.length > 0 ? deduped : ['Spectrum Next register details and bit-level behavior.']; }; const splitLongWord = (word: string, maxLineLength: number) => { if (word.length <= maxLineLength) return [word]; const chunks: string[] = []; for (let idx = 0; idx < word.length; idx += maxLineLength - 1) { chunks.push(`${word.slice(idx, idx + maxLineLength - 1)}-`); } const last = chunks[chunks.length - 1]; chunks[chunks.length - 1] = last.endsWith('-') ? last.slice(0, -1) : last; return chunks; }; const wrapText = (text: string, maxLineLength: number) => { const words = text .split(/\s+/) .filter(Boolean) .flatMap(word => splitLongWord(word, maxLineLength)); const lines: string[] = []; let current = ''; for (const word of words) { const next = current ? `${current} ${word}` : word; if (next.length > maxLineLength && current) { lines.push(current); current = word; } else { current = next; } } if (current) { lines.push(current); } return lines; }; const wrapTextLines = (sourceLines: string[], maxLineLength: number, maxLines: number) => { const output: string[] = []; for (const line of sourceLines) { if (output.length >= maxLines) break; if (!line) { if (output.length > 0 && output[output.length - 1] !== '') { output.push(''); } continue; } const wrapped = wrapText(line, maxLineLength); for (const wrappedLine of wrapped) { if (output.length >= maxLines) break; output.push(wrappedLine); } } return output.slice(0, maxLines); }; export default async function Image({ params }: { params: Promise<{ hex: string }> }) { const { hex } = await params; const targetHex = decodeURIComponent(hex).toLowerCase(); const registers = await getRegisters(); const register = registers.find(r => r.hex_address.toLowerCase() === targetHex); const title = register ? `${register.hex_address} ${register.name}` : 'Spectrum Next Register'; const summaryLinesSource = register ? buildRegisterSummaryLines(register) : ['Register details not found.']; const decAddress = register ? `Dec ${register.dec_address}` : ''; const titleLines = wrapTextLines([title], 32, 2); const summaryLines = wrapTextLines(summaryLinesSource, 54, 6); return new ImageResponse( (
Spectrum Next Explorer
{titleLines.map(line => (
{line}
))}
{summaryLines.map(line => (
{line}
))}
{decAddress}
), { width: size.width, height: size.height, } ); }