import type { Post, Thread } from '../types/api' export interface ExportOptions { format: 'json' | 'html' | 'txt' includeImages?: boolean filename?: string } /** * Export a thread to various formats */ export function exportThread( thread: Thread, board: string, options: ExportOptions ): void { const posts = thread.posts || [] const op = posts[0] const threadTitle = op?.sub || `Thread ${op?.no}` const filename = options.filename || `${board}_${op?.no}_${threadTitle.slice(0, 30).replace(/[^a-zA-Z0-9]/g, '_')}` let content: string let mimeType: string let extension: string switch (options.format) { case 'json': content = JSON.stringify({ board, thread: op?.no, title: threadTitle, exportedAt: new Date().toISOString(), posts: posts.map(p => ({ no: p.no, time: p.time, name: p.name, trip: p.trip, sub: p.sub, com: p.com, filename: p.filename, ext: p.ext, tim: p.tim, w: p.w, h: p.h, replies: p.replies, })), }, null, 2) mimeType = 'application/json' extension = 'json' break case 'html': content = generateHtmlExport(posts, board, threadTitle) mimeType = 'text/html' extension = 'html' break case 'txt': content = generateTextExport(posts, board, threadTitle) mimeType = 'text/plain' extension = 'txt' break default: throw new Error(`Unknown export format: ${options.format}`) } downloadFile(content, `${filename}.${extension}`, mimeType) } function generateHtmlExport(posts: Post[], board: string, title: string): string { const postsHtml = posts.map(post => `
${post.name || 'Anonymous'} ${post.trip ? `${post.trip}` : ''} ${new Date(post.time * 1000).toLocaleString()} No.${post.no}
${post.sub ? `
${post.sub}
` : ''} ${post.tim ? `
${post.filename} ${post.filename}${post.ext}
` : ''} ${post.com ? `
${post.com}
` : ''}
`).join('\n') return ` ${title} - /${board}/

/${board}/ - ${title}

Exported: ${new Date().toLocaleString()} • ${posts.length} posts

${postsHtml} ` } function generateTextExport(posts: Post[], board: string, title: string): string { const lines: string[] = [ `/${board}/ - ${title}`, `Exported: ${new Date().toLocaleString()}`, `${posts.length} posts`, '', '---', '', ] for (const post of posts) { lines.push(`${post.name || 'Anonymous'}${post.trip || ''} - ${new Date(post.time * 1000).toLocaleString()} - No.${post.no}`) if (post.sub) lines.push(`Subject: ${post.sub}`) if (post.tim) lines.push(`Image: https://i.4cdn.org/${board}/${post.tim}${post.ext}`) if (post.com) { // Strip HTML tags for plain text const text = post.com .replace(//gi, '\n') .replace(/<[^>]+>/g, '') .replace(/>/g, '>') .replace(/</g, '<') .replace(/&/g, '&') .replace(/"/g, '"') lines.push(text) } lines.push('') lines.push('---') lines.push('') } return lines.join('\n') } function downloadFile(content: string, filename: string, mimeType: string): void { const blob = new Blob([content], { type: mimeType }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = filename document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) }