From 31522acd04865a753bc3f162bd5480253bdc4718 Mon Sep 17 00:00:00 2001 From: "D. Rimron-Soutter" Date: Thu, 1 Jan 2026 15:54:59 +0000 Subject: [PATCH] missing files --- src/app/registers/[hex]/opengraph-image.tsx | 203 ++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 src/app/registers/[hex]/opengraph-image.tsx diff --git a/src/app/registers/[hex]/opengraph-image.tsx b/src/app/registers/[hex]/opengraph-image.tsx new file mode 100644 index 0000000..20e1d4d --- /dev/null +++ b/src/app/registers/[hex]/opengraph-image.tsx @@ -0,0 +1,203 @@ +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, + } + ); +}