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
Sidebar Adaptation
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
- Phase 1: Deploy unified editor alongside existing editors
- Phase 2: Redirect new editing sessions to unified editor
- Phase 3: Migrate existing editing sessions with user consent
- 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.