mirror of
https://github.com/dergigi/boris.git
synced 2026-02-19 14:04:38 +01:00
feat: add article tags and image alt text to OG metadata
- Add tags field to ArticleMetadata type (extracted from 't' tags) - Add imageAlt field to ArticleMetadata type (uses title as fallback) - Extract 't' tags from article events in fetchArticleMetadataViaRelays - Generate multiple article:tag meta tags in HTML output - Add og:image:alt meta tag for better accessibility This improves SEO and social media previews by including article categorization tags and image descriptions.
This commit is contained in:
@@ -70,6 +70,15 @@ export async function fetchArticleMetadataViaRelays(naddr: string): Promise<Arti
|
||||
const summary = getArticleSummary(article) || 'Read this article on Boris'
|
||||
const image = getArticleImage(article) || '/boris-social-1200.png'
|
||||
|
||||
// Extract 't' tags (topic tags) from article event
|
||||
const tags = article.tags
|
||||
?.filter((tag) => tag[0] === 't' && tag[1])
|
||||
.map((tag) => tag[1])
|
||||
.filter((tag) => tag.length > 0) || []
|
||||
|
||||
// Generate image alt text (use title as fallback)
|
||||
const imageAlt = title || 'Article cover image'
|
||||
|
||||
let authorName = pointer.pubkey.slice(0, 8) + '...'
|
||||
if (profileEvents.length > 0) {
|
||||
const displayName = extractProfileDisplayName(profileEvents[0])
|
||||
@@ -85,7 +94,9 @@ export async function fetchArticleMetadataViaRelays(naddr: string): Promise<Arti
|
||||
summary,
|
||||
image,
|
||||
author: authorName,
|
||||
published: article.created_at
|
||||
published: article.created_at,
|
||||
tags: tags.length > 0 ? tags : undefined,
|
||||
imageAlt
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch article metadata via relays:', err)
|
||||
|
||||
@@ -17,6 +17,12 @@ export function generateHtml(naddr: string, meta: ArticleMetadata | null): strin
|
||||
const description = meta?.summary || 'Your reading list for the Nostr world. A minimal nostr client for bookmark management with highlights.'
|
||||
const image = meta?.image?.startsWith('http') ? meta.image : `${baseUrl}${meta?.image || '/boris-social-1200.png'}`
|
||||
const author = meta?.author || 'Boris'
|
||||
const imageAlt = meta?.imageAlt || title
|
||||
|
||||
// Generate article:tag meta tags
|
||||
const articleTags = meta?.tags && meta.tags.length > 0
|
||||
? meta.tags.map((tag) => ` <meta property="article:tag" content="${escapeHtml(tag)}" />`).join('\n')
|
||||
: ''
|
||||
|
||||
return `<!doctype html>
|
||||
<html lang="en">
|
||||
@@ -39,9 +45,11 @@ export function generateHtml(naddr: string, meta: ArticleMetadata | null): strin
|
||||
<meta property="og:title" content="${escapeHtml(title)}" />
|
||||
<meta property="og:description" content="${escapeHtml(description)}" />
|
||||
<meta property="og:image" content="${escapeHtml(image)}" />
|
||||
<meta property="og:image:alt" content="${escapeHtml(imageAlt)}" />
|
||||
<meta property="og:site_name" content="Boris" />
|
||||
${meta?.published ? `<meta property="article:published_time" content="${new Date(meta.published * 1000).toISOString()}" />` : ''}
|
||||
${meta?.published ? ` <meta property="article:published_time" content="${new Date(meta.published * 1000).toISOString()}" />` : ''}
|
||||
<meta property="article:author" content="${escapeHtml(author)}" />
|
||||
${articleTags}
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
|
||||
@@ -25,6 +25,8 @@ export type ArticleMetadata = {
|
||||
image: string
|
||||
author: string
|
||||
published?: number
|
||||
tags?: string[]
|
||||
imageAlt?: string
|
||||
}
|
||||
|
||||
export async function getArticleMeta(naddr: string): Promise<ArticleMetadata | null> {
|
||||
|
||||
Reference in New Issue
Block a user