diff --git a/src/routes/rss/+server.ts b/src/routes/rss/+server.ts index 147573d..33d312b 100644 --- a/src/routes/rss/+server.ts +++ b/src/routes/rss/+server.ts @@ -15,37 +15,169 @@ function escapeXML(str: string): string { // Helper function to convert content to HTML for full content function convertContentToHTML(content: any): string { - if (!content || !content.blocks) return '' + if (!content) return '' + + // Handle legacy content format (if it's just a string) + if (typeof content === 'string') { + return `
${escapeXML(content)}
` + } + + // Handle TipTap/EditorJS JSON format + if (content.blocks && Array.isArray(content.blocks)) { + return content.blocks + .map((block: any) => { + switch (block.type) { + case 'paragraph': + // Handle both data.text and content formats + const paragraphText = block.data?.text || block.content || '' + return paragraphText ? `${escapeXML(paragraphText)}
` : '' + case 'heading': + case 'header': + const level = block.data?.level || block.level || 2 + const headingText = block.data?.text || block.content || '' + return headingText ? `${escapeXML(code)}` : ''
+ case 'quote':
+ case 'blockquote':
+ const quoteText = block.data?.text || block.content || ''
+ const citation = block.data?.caption || ''
+ if (!quoteText) return ''
+ return `${escapeXML(quoteText)}${citation ? `${escapeXML(citation)}` : ''}` + default: + // Fallback for unknown block types + const defaultText = block.data?.text || block.content || '' + return defaultText ? `
${escapeXML(defaultText)}
` : '' + } + }) + .filter((html: string) => html) // Remove empty blocks + .join('\n') + } + + // Handle TipTap format with doc root + if (content.type === 'doc' && content.content && Array.isArray(content.content)) { + return content.content + .map((node: any) => { + switch (node.type) { + case 'paragraph': + const text = extractTextFromNode(node) + return text ? `${text}
` : '' + case 'heading': + const headingText = extractTextFromNode(node) + const level = node.attrs?.level || 2 + return headingText ? `${quoteText}` : '' + case 'codeBlock': + const code = extractTextFromNode(node) + return code ? `
${code}` : ''
+ case 'image':
+ const src = node.attrs?.src || ''
+ const alt = node.attrs?.alt || ''
+ return src ? `${defaultText}
` : '' + } + }) + .filter((html: string) => html) + .join('\n') + } + + return '' +} - return content.blocks - .map((block: any) => { - switch (block.type) { - case 'paragraph': - return `${escapeXML(block.content || '')}
` - case 'heading': - const level = block.level || 2 - return `${escapeXML(block.content || '')}
` - } - }) - .join('\n') +// Helper to extract text from TipTap nodes +function extractTextFromNode(node: any): string { + if (!node) return '' + + // Direct text content + if (node.text) return escapeXML(node.text) + + // Nested content + if (node.content && Array.isArray(node.content)) { + return node.content + .map((child: any) => { + if (child.type === 'text') { + return escapeXML(child.text || '') + } + return extractTextFromNode(child) + }) + .join('') + } + + return '' } // Helper function to extract text summary from content function extractTextSummary(content: any, maxLength: number = 300): string { - if (!content || !content.blocks) return '' - - const text = content.blocks - .filter((block: any) => block.type === 'paragraph' && block.content) - .map((block: any) => block.content) - .join(' ') - + if (!content) return '' + + let text = '' + + // Handle string content + if (typeof content === 'string') { + text = content + } + // Handle EditorJS format + else if (content.blocks && Array.isArray(content.blocks)) { + text = content.blocks + .filter((block: any) => block.type === 'paragraph') + .map((block: any) => block.data?.text || block.content || '') + .filter((t: string) => t) + .join(' ') + } + // Handle TipTap format + else if (content.type === 'doc' && content.content && Array.isArray(content.content)) { + text = content.content + .filter((node: any) => node.type === 'paragraph') + .map((node: any) => { + if (node.content && Array.isArray(node.content)) { + return node.content + .filter((child: any) => child.type === 'text') + .map((child: any) => child.text || '') + .join('') + } + return '' + }) + .filter((t: string) => t) + .join(' ') + } + + // Clean up and truncate + text = text.replace(/\s+/g, ' ').trim() return text.length > maxLength ? text.substring(0, maxLength) + '...' : text } @@ -77,7 +209,11 @@ export const GET: RequestHandler = async (event) => { section: 'universe', id: post.id.toString(), title: - post.title || `${post.postType.charAt(0).toUpperCase() + post.postType.slice(1)} Post`, + post.title || new Date(post.publishedAt || post.createdAt).toLocaleDateString('en-US', { + year: 'numeric', + month: 'long', + day: 'numeric' + }), description: extractTextSummary(post.content) || '', content: convertContentToHTML(post.content), link: `${event.url.origin}/universe/${post.slug}`, @@ -163,6 +299,10 @@ ${