This application follows a feature-first architecture where related functionality is co-located within feature modules. The codebase has been refactored from monolithic files (2000+ lines) into focused, maintainable modules following the Single Responsibility Principle.
- Features don't import from other features - Each feature is self-contained
- Shared utilities don't import from features - Dependency flow is unidirectional
- App-shell orchestrates features - Only the app-shell layer composes multiple features
- No circular dependencies - Verified with madge
┌─────────────────────────────────────────────────────────┐
│ App Shell │
│ (Orchestration Layer) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Feature Modules │
│ (layout, library, scene, project, generation, etc.) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Shared Modules & Services │
│ (components, hooks, utils, services, ui) │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ Types & Config │
│ (types, config, constants) │
└─────────────────────────────────────────────────────────┘
Each feature module follows a consistent structure:
src/features/<feature-name>/
├── components/ # Feature-specific UI components
├── hooks/ # Feature-specific React hooks
├── state/ # Feature-specific state management (Zustand stores)
├── services/ # Feature-specific business logic
├── utils/ # Feature-specific utilities
├── types.ts # Feature-specific types
├── index.ts # Public API exports
└── README.md # Feature documentation
- app-shell: Main application orchestration and layout composition
- layout: Resizable panel system, collapse/expand, persistence
- library: Project and asset browsing, search, filtering
- scene: Scene management, history, CRUD operations
- project: Project state management and actions
- generation: AI generation services (chat, storyboard, media)
- settings: Application settings and preferences, workflow and template management
- storyboard: Storyboard display and interactions, document management
- chat: Chat interface, message handling, file uploads, streaming responses
Reusable UI components that are used across multiple features:
ProjectManager.tsx- Project list and management UIAssetManager.tsx- Asset browsing and managementLeftManagerDrawer/- Unified drawer with tabstoast/- Toast notification system
Chat Feature (src/features/chat/components/)
AgentDropdown.tsx- Workflow and subtype selectionChatModeDropdown.tsx- Chat mode selection (Simple, Concept, Style)StreamingText.tsx- SSE streaming text displayUploadDropzone.tsx- Drag-and-drop file uploadFileThumb.tsx- File thumbnail with controlsFilePurposeSelector.tsx- File purpose selection modal
Storyboard Feature (src/features/storyboard/components/)
DocumentViewer.tsx- Read-only document viewerDocumentEditor.tsx- Document editing with TiptapTiptapEditor.tsx- Rich text editor integrationDocumentExport.tsx- Export modal (Markdown, PDF, JSON)DocumentHistory.tsx- Version history and restoreDocumentTab.tsx- Document tab for gallery section
Settings Feature (src/features/settings/components/)
WorkflowManager.tsx- Workflow list and managementWorkflowEditor.tsx- Workflow creation/editing formSystemInstructionEditor.tsx- System instruction text editorSubtypeManager.tsx- Subtype list and managementTemplateLibrary.tsx- Style template gridTemplateEditor.tsx- Template creation/editing formTemplateCard.tsx- Template card component
Shared React hooks:
useSearchState.ts- Debounced search with localStorage persistenceuseProjectState.ts- Legacy shim for backward compatibilityuseTheme.ts- Theme management
Core services and registries:
registry.ts- Service provider and dependency injection- Storage services, API clients, etc.
Low-level UI primitives:
- Icons, buttons, inputs, badges, etc.
- Reusable across all features
Shared utility functions:
errorMapper.ts- Error handling and mappingsceneStateMachine.ts- Scene lifecycle managementsseClient.ts- Server-Sent Events client wrapperfileUpload.ts- File upload with progress trackingdocumentExport.ts- Document export utilitieserrorHandling.ts- Enhanced error handling with request IDs- Storage utilities, formatters, etc.
Application-wide configuration:
layout.ts- Layout constants and defaultspresetStyles.ts- Style presetsstorageKeys.ts- localStorage key definitions
Shared TypeScript types and interfaces:
index.ts- Core domain typesservices.ts- Service-related types
-
Features can import from:
- Their own modules (internal imports)
- Shared modules (
components/,hooks/,services/,ui/,utils/) - Types and config (
types/,config/)
-
App-shell can import from:
- Any feature module (orchestration layer)
- Shared modules
- Types and config
-
Shared modules can import from:
- Other shared modules (with care to avoid circular deps)
- Types and config
- NOT from features
-
Features CANNOT import from other features
- ❌
src/features/library/importing fromsrc/features/scene/ - ✅ Extract shared logic to
src/hooks/orsrc/utils/
- ❌
-
Shared modules CANNOT import from features
- ❌
src/components/importing fromsrc/features/library/ - ✅ Pass data as props or use dependency injection
- ❌
-
Circular dependencies are prohibited
- Verified with
npx madge --circular --extensions ts,tsx src/
- Verified with
State is managed using Zustand with a modular slice pattern:
// Store composition pattern
export const useProjectStore = create<ProjectStore>((set, get) => ({
// Core state
projects: [],
activeProjectId: null,
// Compose slices
...createSceneSlice(set, get),
...createGroupSlice(set, get),
...createTagSlice(set, get),
...createChatSlice(set, get),
}));Each domain has its own slice:
sceneStore.ts- Scene operations (< 300 lines)groupStore.ts- Group operations (< 200 lines)tagStore.ts- Tag operations (< 200 lines)chatStore.ts- Chat operations (< 200 lines)storeUtils.ts- Shared store utilities (< 200 lines)
Settings Store (src/features/settings/state/)
workflowStore.ts- Workflow CRUD operationstemplateStore.ts- Style template CRUD operationssettingsStore.ts- Composed settings state
Storyboard Store (src/features/storyboard/state/)
documentStore.ts- Document CRUD, versioning, auto-save
Generation Store (src/features/generation/state/)
fileUploadStore.ts- File upload progress and management
Complex state is composed from smaller, focused hooks:
// Main orchestration hook
export const useAppShellState = () => {
const projectState = useProjectState();
const layout = useLayout();
const sceneActions = useSceneActions();
const sceneManager = useSceneManager();
// ... compose all features
return {
project: projectState,
layout,
scene: { actions: sceneActions, manager: sceneManager },
// ...
};
};Feature components are self-contained and receive all dependencies via props:
// Good: All dependencies passed as props
export const LibraryPanel: React.FC<LibraryPanelProps> = ({
projects,
scenes,
onSelectProject,
onProjectAction,
}) => {
// Feature-specific state
const library = useLibraryState();
return (/* ... */);
};Layout is separated into mobile and desktop variants:
MobileLayout.tsx- Mobile-specific renderingDesktopLayout.tsx- Desktop-specific renderingAppSidebar.tsx- Sidebar component
The app-shell composes features without implementing business logic:
export const AppShell: React.FC = () => {
// Single orchestration hook
const appState = useAppShellState();
// Simple theme management
const { theme, toggleTheme } = useTheme();
// Determine layout
const isMobile = appState.layout.isMobileLayout;
return (
<div className="app-container">
{isMobile ? (
<MobileLayout appState={appState} theme={theme} />
) : (
<DesktopLayout appState={appState} theme={theme} />
)}
</div>
);
};To maintain readability and testability:
- Components: < 300 lines (AppShell, LeftManagerDrawer)
- Hooks: < 200 lines each
- Store files: < 500 lines each
- Utility files: < 200 lines each
When a file exceeds these limits, consider:
- Extracting sub-components
- Splitting into multiple hooks
- Creating store slices
- Moving shared logic to utilities
- Test hooks in isolation with mocked dependencies
- Test components with mocked props
- Test store slices independently
- Test feature orchestration in app-shell
- Test cross-feature interactions
- Test state synchronization
Run circular dependency check:
npx madge --circular --extensions ts,tsx src/
npx madge --circular --extensions ts,tsx server/If you find yourself wanting to import from another feature:
- Identify the shared logic - What needs to be reused?
- Extract to shared module - Move to
hooks/,utils/, orcomponents/ - Update imports - Both features import from shared location
- Verify no circular deps - Run madge
Example:
// Before: Feature importing from another feature
// src/components/ProjectManager.tsx
import { useLibrarySearchState } from "../features/library/hooks/useLibrarySearchState";
// After: Both import from shared location
// src/hooks/useSearchState.ts (new shared hook)
export function useSearchState<T>() { /* ... */ }
// src/features/library/hooks/useLibrarySearchState.ts (re-export for compatibility)
export { useSearchState as useLibrarySearchState } from "../../../hooks/useSearchState";
// src/components/ProjectManager.tsx
import { useSearchState } from "../hooks/useSearchState";- Create feature directory:
src/features/<feature-name>/ - Add standard subdirectories:
components/,hooks/,state/ - Create
index.tsto export public API - Add
README.mddocumenting the feature - Import only from shared modules and types
- Integrate in app-shell if needed
- Identify responsibilities - What does this file do?
- Extract hooks - Move state logic to custom hooks
- Extract components - Move UI sections to components
- Extract utilities - Move pure functions to utils
- Compose - Use extracted pieces in main file
- Verify - Run tests and check file sizes
- Keep files focused and under size limits
- Use composition over inheritance
- Pass dependencies via props or hooks
- Extract shared logic to shared modules
- Document feature boundaries in README files
- Run circular dependency checks regularly
- Don't import features from other features
- Don't import features from shared modules
- Don't create circular dependencies
- Don't mix business logic with UI rendering
- Don't exceed file size guidelines
- Don't skip documentation
Business logic and external service integration:
AI Services (geminiClient.ts)
streamChatResponse()- SSE streaming chat with token bufferinggenerateEnhancedStoryboard()- Scene generation with auto-animation promptsgenerateStylePreviews()- Generate 4 style preview scenesgenerateSceneImage()- Image generation with multiple modelsgenerateSceneVideo()- Video generation (Veo 2.0, Veo 3.1)editSceneImage()- Image editing with Gemini 2.5 Flash
File Services (fileUploadService.ts)
uploadFile()- Size-based routing (<20MB inline, ≥20MB Files API)getFileById()- Retrieve file metadatadeleteFile()- Delete file and cleanup Files API resources- Automatic thumbnail generation
- Video/audio always routed to Files API
Document Services (documentService.ts)
getDocument()- Retrieve project documentsaveDocument()- Save with auto-versioning (keep last 10)getDocumentHistory()- List version historyrestoreDocumentVersion()- Restore previous versionexportDocument()- Export as Markdown, PDF, or JSON
Database access layer with SQLite:
Core Stores
projectStore.ts- Project CRUD operationsassetStore.ts- Asset managementsceneStore.ts- Scene operations
New Stores
workflowStore.ts- Workflow and subtype CRUDtemplateStore.ts- Style template CRUDdocumentStore.ts- Document versioning and historyuploadedFilesStore.ts- File metadata persistence
Express route handlers with validation:
Core Routes
projects.ts- Project CRUD, document management, statsassets.ts- Asset upload and managementai.ts- AI generation gateway with SSE streaming
New Routes
workflows.ts- Workflow and subtype CRUD endpointstemplates.ts- Style template CRUD endpointsfiles.ts- File upload with multer integration
AI Telemetry (aiTelemetry.ts)
- Request ID generation
- Structured logging with Pino
- Error context tracking
- Prompt hashing for privacy
Rate Limiting (rateLimiter.ts)
- Token bucket algorithm
- Per-IP rate limiting
- Configurable window and limits
- Rate limit headers
Asset Persistence (assetPersistence.ts)
- File system operations
- Directory management
- Asset cleanup
Scene Enrichment (sceneEnrichment.ts)
- Add asset URLs to scenes
- Enrich with metadata
- Format for API responses
Core Tables
projects- Project metadatascenes- Scene data with durationassets- Generated media filesscene_groups- Scene groupingscene_tags- Scene tagging
New Tables
workflows- AI agent workflowsworkflow_subtypes- Workflow variationsstyle_templates- Visual style templatesproject_documents- Versioned project documentsuploaded_files- User-uploaded reference files
Migration-based schema management:
001_initial_schema.sql- Core tables002_scene_groups.sql- Scene grouping003_scene_tags.sql- Scene tagging004_project_documents.sql- Document versioning005_workflows.sql- Workflow system006_workflow_subtypes.sql- Workflow variations007_style_templates.sql- Style templates008_uploaded_files.sql- File uploads009_seed_workflows.sql- Default workflows010_seed_style_templates.sql- Default templates011_add_scene_duration.sql- Scene duration tracking
# Check for circular dependencies
npx madge --circular --extensions ts,tsx src/
npx madge --circular --extensions ts,tsx server/
# Type checking
npm run typecheck
npm run typecheck:server
# Run tests
npm test
npm run test:api
# Build verification
npm run build:all
# Database operations
npm run migrate # Apply migrations
npm run seed # Apply migrations + seed data
npm run check:db # Verify database integrity- Configuration Guide
- Feature-First Structure ADR
- Project Plan
- Feature READMEs in
src/features/*/README.md