Skip to Content
📚 MyStoryFlow Docs — Your guide to preserving family stories
Technical DocumentationUnified Editor System

Unified Editor System Architecture

Overview

The MyStoryFlow Unified Editor System provides a single, powerful editing experience that adapts contextually for stories, chapters, and books while preserving content integrity and maintaining feature consistency across all editing modes.

Core Architecture

Context-Aware Editor

The system is built around a single EnhancedEditorShell component that dynamically adapts based on editing context:

interface EditorContext { mode: 'story' | 'chapter' | 'book' contentId: string parentId?: string // campaign_id for stories, book_id for chapters templateContext?: string preserveContent?: boolean metadata?: { title?: string author?: string bookType?: string campaignId?: string } }

Unified Routing System

All editing flows through a single route pattern:

/editor/[...params]

Route Mapping:

  • /editor/story/[id] → Story editing mode
  • /editor/chapter/[bookId]/[chapterId] → Chapter editing mode
  • /editor/book/[id] → Book editing mode
  • /editor/campaign/[campaignId]/story/[storyId] → Campaign story editing

Legacy Route Redirects:

  • /stories/[id]/edit/editor/story/[id]
  • /campaigns/[id]/stories/[storyId]/edit/editor/campaign/[id]/story/[storyId]

Component Architecture

1. Context Detection Layer

const EditorContextProvider = ({ children, params }) => { const context = useMemo(() => { return resolveEditorContext(params) }, [params]) return ( <EditorContext.Provider value={context}> {children} </EditorContext.Provider> ) }

2. Adaptive Interface Components

const AdaptiveSidebar = () => { const { mode } = useEditorContext() switch (mode) { case 'story': return <StorySidebar /> // Templates, Media, Family case 'chapter': return <ChapterSidebar /> // Story Linking, Content Blocks, Navigation case 'book': return <BookSidebar /> // Chapter Management, Publishing, ToC } }

Right Panel Adaptation

const AdaptiveRightPanel = () => { const { mode } = useEditorContext() const availableTools = { story: ['preview', 'ai', 'grammar', 'spellcheck'], chapter: ['book-preview', 'cross-references', 'version-history', 'ai'], book: ['full-preview', 'export', 'kdp-validation', 'analytics'] } return <RightPanel tools={availableTools[mode]} /> }

3. Content Management Layer

Unified Auto-Save System

const useUnifiedAutoSave = (context: EditorContext) => { const saveFunction = useMemo(() => { switch (context.mode) { case 'story': return saveStoryContent case 'chapter': return saveChapterContent case 'book': return saveBookContent } }, [context.mode]) return createDebouncedSave(saveFunction, 500) }

Context-Aware Storage

const saveContent = async (context: EditorContext, content: string) => { const supabase = await createClient() switch (context.mode) { case 'story': return supabase .from('stories') .update({ content }) .eq('id', context.contentId) case 'chapter': return supabase .from('book_chapters') .update({ content }) .eq('id', context.contentId) case 'book': // Handle book-level metadata updates return updateBookMetadata(context.contentId, content) } }

TipTap Editor Configuration

Unified Extension System

All content blocks and extensions are available across all editing modes:

const getUnifiedExtensions = (context: EditorContext) => [ StarterKit, Image, Link, Highlight, Underline, // Content Blocks (available in all modes) ChapterOpeningBlock, HighlightedMemoryBlock, LifeLessonBlock, FamilyRecipeBlock, LetterBlock, QuoteBlock, TimelineBlock, QABlock, ImageCaptionBlock, ImageGridBlock, FullWidthImageBlock, PhotoCollageBlock, MapStoryBlock, PoetryVerseBlock, SidebarNoteBlock, DecorativeBorderBlock, BeforeAfterBlock, FamilyTreeBlock, StatisticsBlock, // Context-specific extensions ...(context.mode === 'book' ? [TableOfContentsBlock, IndexBlock, BibliographyBlock] : []), ...(context.mode === 'chapter' ? [ChapterNavigationBlock, CrossReferenceBlock] : []) ]

Content Block Registry Enhancement

export interface ContentBlockDefinition { id: string name: string description: string icon: string category: 'text' | 'media' | 'interactive' | 'special' | 'design' | 'book-specific' color: string component: string insertCommand?: string defaultContent?: Record<string, unknown> availableInModes: ('story' | 'chapter' | 'book')[] exportFormats: ('pdf' | 'epub' | 'kdp')[] publishingMetadata?: { kdpCompliant: boolean pageBreakBehavior: 'before' | 'after' | 'avoid' | 'auto' marginRequirements?: string } }

Content Preservation System

Cross-Context Transitions

const transitionContent = async ( fromContext: EditorContext, toContext: EditorContext, content: string ) => { // Preserve all content during context switches const preservedContent = { originalContent: content, sourceContext: fromContext, targetContext: toContext, transitionTime: new Date(), preservedBlocks: extractContentBlocks(content) } // Store transition record for rollback await storeTransitionRecord(preservedContent) // Apply context-specific formatting return formatContentForContext(content, toContext) }

Version History Management

const saveContentVersion = async ( context: EditorContext, content: string, changeSummary?: string ) => { const versionData = { content_id: context.contentId, content_type: context.mode, content_snapshot: content, content_blocks: extractContentBlocks(content), change_summary: changeSummary, created_at: new Date(), created_by: context.userId } return supabase .from('content_versions') .insert(versionData) }

Performance Optimizations

Lazy Loading by Context

const LazyContextComponents = { story: lazy(() => import('./components/StoryModeComponents')), chapter: lazy(() => import('./components/ChapterModeComponents')), book: lazy(() => import('./components/BookModeComponents')) }

Content Block Virtualization

For large documents, content blocks are virtualized to maintain performance:

const VirtualizedEditor = ({ content, context }) => { const visibleBlocks = useVirtualization(content, context) return ( <div className="editor-viewport"> {visibleBlocks.map(block => ( <ContentBlock key={block.id} {...block} /> ))} </div> ) }

Error Handling & Recovery

Context Validation

const validateEditorContext = (context: EditorContext): ValidationResult => { const errors = [] if (!context.contentId) { errors.push('Content ID is required') } if (context.mode === 'chapter' && !context.parentId) { errors.push('Book ID is required for chapter editing') } return { isValid: errors.length === 0, errors } }

Auto-Recovery System

const useAutoRecovery = (context: EditorContext) => { useEffect(() => { const recoveryData = localStorage.getItem(`editor-recovery-${context.contentId}`) if (recoveryData && context.preserveContent) { const { content, timestamp } = JSON.parse(recoveryData) // Offer recovery if content is recent (< 1 hour old) if (Date.now() - timestamp < 3600000) { showRecoveryDialog(content) } } }, [context]) }

Integration Points

Template System Integration

The unified editor seamlessly integrates with the template component system:

const applyTemplate = async ( context: EditorContext, templateId: string ) => { const template = await loadTemplate(templateId, context.mode) const formattedContent = await formatTemplateForContext(template, context) editor.commands.setContent(formattedContent) // Track template usage await trackTemplateUsage(templateId, context.mode, context.contentId) }

Publishing Pipeline Integration

const exportContent = async ( context: EditorContext, format: 'pdf' | 'epub' | 'kdp' ) => { const content = editor.getHTML() const processedContent = await processContentForExport(content, context, format) return generateExport(processedContent, format, context.metadata) }

Testing Strategy

Context Switching Tests

describe('Context Switching', () => { it('preserves content when switching from story to chapter mode', async () => { const originalContent = 'Test content with <strong>formatting</strong>' await switchContext('story', 'chapter', originalContent) expect(getEditorContent()).toBe(originalContent) }) })

Auto-Save Tests

describe('Unified Auto-Save', () => { it('saves to correct table based on context', async () => { const storyContext = { mode: 'story', contentId: 'story-123' } const chapterContext = { mode: 'chapter', contentId: 'chapter-456' } await testAutoSave(storyContext, 'stories') await testAutoSave(chapterContext, 'book_chapters') }) })

Migration Strategy

Gradual Rollout

  1. Phase 1: Deploy unified editor alongside existing editors
  2. Phase 2: Redirect new editing sessions to unified editor
  3. Phase 3: Migrate existing editing sessions with user consent
  4. Phase 4: Deprecate old editors and routes

Backward Compatibility

All existing routes and functionality remain available during migration period with automatic redirects to maintain user workflows.