Skip to Content
📚 MyStoryFlow Docs — Your guide to preserving family stories
Technical DocumentationBook Publishing Architecture

Book Publishing Architecture: MyStoryFlow MVP Implementation

Executive Summary

This document outlines the technical architecture for MyStoryFlow’s book publishing system, designed to transform user stories into print-ready books. The architecture leverages existing TipTap infrastructure with enhanced preview capabilities, following proven patterns from professional publishing tools.

Architecture Overview

System Components

Core Architecture Principles

1. Separation of Concerns

  • Content Creation: TipTap editor optimized for writing flow
  • Layout Processing: Preview system handles pagination and formatting
  • Output Generation: Export pipeline produces print-ready files

2. Content-First Philosophy

  • Users focus on story content without pagination distractions
  • Rich content blocks maintain semantic integrity
  • Layout decisions made during preview/export phase

3. Progressive Enhancement

  • MVP delivers core book publishing functionality
  • Advanced features added incrementally
  • Existing editor system enhanced, not replaced

Technical Implementation

Component Architecture

1. Enhanced TipTap Editor

// Enhanced editor configuration const bookEditor = useEditor({ extensions: [ // Core editing extensions StarterKit.configure({ paragraph: { HTMLAttributes: { class: 'book-paragraph' } }, heading: { levels: [1, 2, 3] } }), // Typography optimized for books Typography.configure({ openDoubleQuote: '"', closeDoubleQuote: '"', openSingleQuote: ''', closeSingleQuote: ''' }), // Rich content blocks for stories QuoteBlock.configure({ HTMLAttributes: { class: 'story-quote' } }), FamilyRecipeBlock.configure({ HTMLAttributes: { class: 'family-recipe' } }), ImageBlock.configure({ allowBase64: false, // Require proper image URLs HTMLAttributes: { class: 'story-image' } }), // Pagination extension (preview only) TipTapPagination.configure({ mode: 'preview-only', pageFormat: { width: '6in', height: '9in', margins: { top: '0.75in', bottom: '0.75in', left: '0.75in', right: '0.75in' } } }) ], // Performance optimization editorProps: { attributes: { class: 'book-editor prose max-w-none' } } })

2. Content Processing Pipeline

interface ContentProcessor { // Process raw editor content into book structure processContent(editorContent: JSONContent): BookContent // Extract chapters from story structure extractChapters(content: BookContent): Chapter[] // Generate table of contents generateTOC(chapters: Chapter[]): TOCEntry[] // Prepare rich content for pagination prepareRichContent(blocks: ContentBlock[]): ProcessedBlock[] } class BookContentProcessor implements ContentProcessor { processContent(editorContent: JSONContent): BookContent { return { frontMatter: this.extractFrontMatter(editorContent), chapters: this.extractChapters(editorContent), backMatter: this.extractBackMatter(editorContent), metadata: this.extractMetadata(editorContent) } } private extractChapters(content: JSONContent): Chapter[] { // Logic to identify chapter breaks (H1 headings) // Convert content blocks to chapter structure // Preserve rich content block integrity } }

3. Smart Pagination Engine

interface PaginationEngine { // Calculate page breaks for processed content calculatePageBreaks(content: BookContent): PageBreak[] // Handle rich content block pagination paginateRichContent(block: ProcessedBlock): BlockPagination // Generate page-specific content generatePageContent(pageNumber: number): PageContent } class SmartPaginationEngine implements PaginationEngine { private paginationRules: PaginationRules = { // Rich content handling recipeBlocks: { keepTogether: ['title', 'ingredients'], allowBreak: 'ingredients-instructions', minimumLines: 3 }, quoteBlocks: { singlePageThreshold: 4, keepWithAttribution: true, preserveFormatting: true }, imageBlocks: { maxPageHeight: 0.75, keepWithCaption: true, fallbackBehavior: 'moveToNextPage' }, // Typography rules orphanControl: 2, widowControl: 2, // Chapter handling chapterBreaks: 'always-new-page', chapterTitles: 'keep-with-first-paragraph' } calculatePageBreaks(content: BookContent): PageBreak[] { // Implementation of smart page break algorithm // Considers content type, formatting, and readability } }

4. Book Preview Component

interface BookPreviewProps { content: BookContent format: BookFormat showMode: 'single-page' | 'facing-pages' | 'continuous' onPageChange?: (pageNumber: number) => void } const BookPreview: React.FC<BookPreviewProps> = ({ content, format, showMode = 'single-page' }) => { const { pages, totalPages } = usePagination(content, format) const [currentPage, setCurrentPage] = useState(1) return ( <div className="book-preview"> {/* Book format styling */} <style jsx>{` .book-page { width: ${format.width}; height: ${format.height}; margin: ${format.margins}; font-family: ${format.typography.fontFamily}; font-size: ${format.typography.fontSize}; line-height: ${format.typography.lineHeight}; } `}</style> {/* Preview pages */} <div className="preview-container"> {showMode === 'single-page' ? ( <BookPage content={pages[currentPage - 1]} pageNumber={currentPage} format={format} /> ) : ( pages.map((page, index) => ( <BookPage key={index} content={page} pageNumber={index + 1} format={format} /> )) )} </div> {/* Preview controls */} <BookPreviewControls currentPage={currentPage} totalPages={totalPages} onPageChange={setCurrentPage} onFormatChange={/* ... */} /> </div> ) }

Book Format Specifications

Standard Trim Sizes

export const BOOK_FORMATS = { '6x9': { width: '6in', height: '9in', margins: { top: '0.75in', bottom: '0.75in', inner: '0.875in', // Binding side outer: '0.625in' // Outer edge }, gutterMargin: '0.25in' }, '5.5x8.5': { width: '5.5in', height: '8.5in', margins: { top: '0.75in', bottom: '0.75in', inner: '0.75in', outer: '0.5in' } }, '5x8': { width: '5in', height: '8in', margins: { top: '0.625in', bottom: '0.625in', inner: '0.75in', outer: '0.5in' } } } as const

Typography Configuration

export const BOOK_TYPOGRAPHY = { fonts: { body: { family: 'Crimson Text, Georgia, serif', size: '11pt', lineHeight: 1.4, letterSpacing: '0.01em' }, headings: { family: 'Source Sans Pro, -apple-system, sans-serif', chapterTitle: { size: '18pt', weight: 600, letterSpacing: '0.02em', marginTop: '2in', marginBottom: '0.5in' }, sectionHeading: { size: '14pt', weight: 500, marginTop: '1.5em', marginBottom: '0.75em' } }, quotes: { family: 'inherit', size: '10pt', style: 'italic', indent: '0.5in', marginTop: '1em', marginBottom: '1em' } }, spacing: { paragraphSpacing: '0em', // No space between paragraphs paragraphIndent: '0.25in', // First line indent chapterSpacing: '2in', // Space before chapter title sectionSpacing: '1.5em' // Space before section headings } }

Front Matter & Book Structure

Automatic Front Matter Generation

interface FrontMatter { titlePage: TitlePageConfig copyright: CopyrightConfig dedication?: string tableOfContents: TOCConfig acknowledgments?: string } class FrontMatterGenerator { generateTitlePage(bookMetadata: BookMetadata): TitlePageContent { return { title: bookMetadata.title, subtitle: bookMetadata.subtitle, author: bookMetadata.author, publisher: bookMetadata.publisher || 'MyStoryFlow Publishing', publishDate: bookMetadata.publishDate || new Date() } } generateTableOfContents(chapters: Chapter[]): TOCEntry[] { return chapters.map((chapter, index) => ({ title: chapter.title, pageNumber: chapter.startPage, level: chapter.level, children: chapter.sections?.map(section => ({ title: section.title, pageNumber: section.startPage, level: section.level })) })) } generateCopyrightPage(bookMetadata: BookMetadata): CopyrightContent { return { copyrightNotice: `© ${new Date().getFullYear()} ${bookMetadata.author}`, isbn: bookMetadata.isbn, publisher: bookMetadata.publisher, edition: bookMetadata.edition || '1st Edition', printingInfo: 'Printed in the United States of America', additionalInfo: bookMetadata.copyrightInfo } } }

Rich Content Block Handling

Recipe Block Pagination

class RecipeBlockPaginator { paginateRecipe(recipe: FamilyRecipeBlock): RecipePageLayout { const components = { title: recipe.title, description: recipe.description, ingredients: recipe.ingredients, instructions: recipe.instructions, notes: recipe.notes, attribution: recipe.attribution } // Calculate space requirements const spaceNeeds = this.calculateSpaceNeeds(components) // Apply pagination rules if (spaceNeeds.total < this.availablePageSpace * 0.8) { // Keep entire recipe on one page return { layout: 'single-page', components } } else { // Smart break between ingredients and instructions return { layout: 'multi-page', page1: [components.title, components.description, components.ingredients], page2: [components.instructions, components.notes, components.attribution] } } } }

Quote Block Formatting

class QuoteBlockFormatter { formatQuote(quote: QuoteBlock): FormattedQuote { return { content: quote.content, attribution: quote.attribution, formatting: { indent: '0.5in', fontSize: '10pt', fontStyle: 'italic', marginTop: '1em', marginBottom: '1em', // Ensure quote and attribution stay together pageBreakInside: 'avoid' } } } }

Export Pipeline

PDF Generation

interface PDFExporter { generatePDF(bookContent: BookContent, format: BookFormat): Promise<Buffer> generatePrintReady(bookContent: BookContent, printSpecs: PrintSpecs): Promise<Buffer> } class BookPDFExporter implements PDFExporter { async generatePDF(bookContent: BookContent, format: BookFormat): Promise<Buffer> { // Use puppeteer or similar for high-quality PDF generation const html = this.renderBookHTML(bookContent, format) const pdfOptions = { format: 'A4', // Or custom dimensions margin: { top: format.margins.top, bottom: format.margins.bottom, left: format.margins.inner, right: format.margins.outer }, printBackground: true, displayHeaderFooter: true, headerTemplate: this.generateHeaderTemplate(bookContent), footerTemplate: this.generateFooterTemplate(bookContent) } return await this.browser.pdf(pdfOptions) } private renderBookHTML(content: BookContent, format: BookFormat): string { // Render complete book HTML with CSS print styles return ` <!DOCTYPE html> <html> <head> <title>${content.metadata.title}</title> ${this.generatePrintCSS(format)} </head> <body> ${this.renderFrontMatter(content.frontMatter)} ${this.renderChapters(content.chapters)} ${this.renderBackMatter(content.backMatter)} </body> </html> ` } }
export const PRINT_SPECIFICATIONS = { colorSpace: 'CMYK', resolution: 300, // DPI bleed: '0.125in', safetyMargin: '0.125in', imageRequirements: { minResolution: 300, maxFileSize: '10MB', acceptedFormats: ['JPEG', 'PNG', 'TIFF'] }, fontEmbedding: { embedAll: true, subsetFonts: true, createOutlines: false // Preserve text selectability } }

Performance Optimization

Lazy Loading Strategy

class BookPreviewOptimizer { // Only render visible pages plus buffer private renderBuffer = 2 // Pages before/after current view getVisiblePages(currentPage: number, totalPages: number): number[] { const start = Math.max(1, currentPage - this.renderBuffer) const end = Math.min(totalPages, currentPage + this.renderBuffer) return Array.from({ length: end - start + 1 }, (_, i) => start + i) } // Virtualize page rendering for large books renderVirtualizedPages(pages: PageContent[]): VirtualizedPageList { // Implementation similar to react-window // Only render pages in viewport + buffer } }

Caching Strategy

class BookRenderCache { private pageCache = new Map<string, RenderedPage>() private paginationCache = new Map<string, PageBreak[]>() // Cache rendered pages by content hash getCachedPage(contentHash: string): RenderedPage | null { return this.pageCache.get(contentHash) || null } // Cache pagination calculations getCachedPagination(contentHash: string): PageBreak[] | null { return this.paginationCache.get(contentHash) || null } // Invalidate cache when content changes invalidateCache(contentId: string): void { // Remove related cache entries } }

Integration with Existing System

Database Schema Extensions

-- Extend existing stories table for book publishing ALTER TABLE stories ADD COLUMN IF NOT EXISTS book_metadata JSONB DEFAULT '{}'; ALTER TABLE stories ADD COLUMN IF NOT EXISTS front_matter JSONB DEFAULT '{}'; ALTER TABLE stories ADD COLUMN IF NOT EXISTS publication_settings JSONB DEFAULT '{}'; -- New table for book formats and templates CREATE TABLE IF NOT EXISTS book_formats ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, display_name TEXT NOT NULL, dimensions JSONB NOT NULL, -- { width, height, margins } typography JSONB NOT NULL, -- Font settings is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW() ); -- Book export history CREATE TABLE IF NOT EXISTS book_exports ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), story_id UUID REFERENCES stories(id) ON DELETE CASCADE, user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE, format_id UUID REFERENCES book_formats(id), export_type TEXT NOT NULL, -- 'pdf', 'epub', 'print' file_url TEXT, export_settings JSONB DEFAULT '{}', created_at TIMESTAMPTZ DEFAULT NOW() );

API Endpoints

// Book publishing endpoints export const bookAPI = { // Preview endpoints '/api/books/preview': { POST: (storyId: string, format: BookFormat) => BookPreview }, // Export endpoints '/api/books/export/pdf': { POST: (storyId: string, options: PDFOptions) => Buffer }, '/api/books/export/epub': { POST: (storyId: string, options: EPUBOptions) => Buffer }, // Format management '/api/books/formats': { GET: () => BookFormat[], POST: (format: BookFormat) => BookFormat } }

Testing Strategy

Unit Tests

  • Content processing functions
  • Pagination algorithm accuracy
  • Rich content block handling
  • Typography calculations

Integration Tests

  • End-to-end book publishing workflow
  • Preview accuracy vs PDF output
  • Performance with large documents
  • Cross-browser compatibility

Visual Regression Tests

  • Page layout consistency
  • Typography rendering
  • Rich content formatting
  • Print preview accuracy

Deployment Strategy

Feature Flags

export const BOOK_PUBLISHING_FLAGS = { enableBookPreview: true, enablePDFExport: true, enableAdvancedTypography: false, // Future feature enableCollaborativeEditing: false // Future feature }

Rollout Plan

  1. Week 1: Internal testing with small documents
  2. Week 2: Beta testing with selected users
  3. Week 3: Gradual rollout to all users
  4. Week 4: Performance monitoring and optimization

Future Enhancements

Advanced Features (Post-MVP)

  • Multi-format support: EPUB, MOBI, hardcover specifications
  • Advanced typography: Drop caps, pull quotes, custom fonts
  • Collaborative editing: Real-time collaboration on book projects
  • Professional templates: Genre-specific book layouts
  • Print on demand: Integration with printing services

Performance Improvements

  • WebAssembly: High-performance pagination calculations
  • Service workers: Offline book editing and preview
  • CDN optimization: Fast loading of book assets and fonts

This architecture provides a solid foundation for MyStoryFlow’s book publishing capabilities while maintaining flexibility for future enhancements and optimizations.


This document serves as the technical specification for MyStoryFlow’s book publishing system. Last updated: January 2025