# Declarative System Documentation ## Overview CodeForge now uses a **declarative, JSON-driven architecture** that makes it easy to add, modify, and configure pages without touching core application code. This system reduces complexity, improves maintainability, and makes the codebase more scalable. ## Key Benefits ✅ **Add new pages by editing a JSON file** - no need to modify App.tsx ✅ **Dynamic component loading** - components are lazy-loaded based on configuration ✅ **Automatic keyboard shortcuts** - defined in JSON, automatically wired up ✅ **Feature toggle integration** - pages automatically show/hide based on feature flags ✅ **Consistent page structure** - all pages follow the same rendering pattern ✅ **Easy to test and maintain** - configuration is separate from implementation ## Architecture ### Configuration Files #### `/src/config/pages.json` The main configuration file that defines all pages in the application. ```json { "pages": [ { "id": "dashboard", "title": "Dashboard", "icon": "ChartBar", "component": "ProjectDashboard", "enabled": true, "shortcut": "ctrl+1", "order": 1 }, { "id": "code", "title": "Code Editor", "icon": "Code", "component": "CodeEditor", "enabled": true, "toggleKey": "codeEditor", "shortcut": "ctrl+2", "order": 2, "requiresResizable": true } ] } ``` #### Page Configuration Schema | Field | Type | Required | Description | |-------|------|----------|-------------| | `id` | string | ✅ | Unique identifier for the page (used in routing) | | `title` | string | ✅ | Display name for the page | | `icon` | string | ✅ | Phosphor icon name | | `component` | string | ✅ | Component name (must exist in componentMap) | | `enabled` | boolean | ✅ | Whether the page is available | | `toggleKey` | string | ❌ | Feature toggle key (from FeatureToggles type) | | `shortcut` | string | ❌ | Keyboard shortcut (e.g., "ctrl+1", "ctrl+shift+e") | | `order` | number | ✅ | Display order in navigation | | `requiresResizable` | boolean | ❌ | Special flag for split-pane layouts | ### Core Functions #### `getPageConfig()` Returns the complete page configuration. ```typescript import { getPageConfig } from '@/config/page-loader' const config = getPageConfig() // Returns: { pages: PageConfig[] } ``` #### `getEnabledPages(featureToggles)` Returns only pages that are enabled and pass feature toggle checks. ```typescript import { getEnabledPages } from '@/config/page-loader' const enabledPages = getEnabledPages(featureToggles) // Returns: PageConfig[] ``` #### `getPageShortcuts(featureToggles)` Returns keyboard shortcuts for enabled pages. ```typescript import { getPageShortcuts } from '@/config/page-loader' const shortcuts = getPageShortcuts(featureToggles) // Returns: Array<{ key: string, ctrl?: boolean, shift?: boolean, description: string, action: string }> ``` ## How to Add a New Page ### Step 1: Create Your Component ```typescript // src/components/MyNewDesigner.tsx export function MyNewDesigner() { return (

My New Designer

) } ``` ### Step 2: Add to Component Map In `src/App.tsx`, add your component to the `componentMap`: ```typescript const componentMap: Record> = { // ... existing components MyNewDesigner: lazy(() => import('@/components/MyNewDesigner').then(m => ({ default: m.MyNewDesigner }))), } ``` ### Step 3: Add to pages.json Add your page configuration to `/src/config/pages.json`: ```json { "id": "my-new-page", "title": "My New Designer", "icon": "Sparkle", "component": "MyNewDesigner", "enabled": true, "toggleKey": "myNewFeature", "shortcut": "ctrl+shift+n", "order": 21 } ``` ### Step 4: (Optional) Add Feature Toggle If using a feature toggle, add it to the `FeatureToggles` type in `/src/types/project.ts`: ```typescript export interface FeatureToggles { // ... existing toggles myNewFeature: boolean } ``` And update the default in `/src/hooks/use-project-state.ts`: ```typescript const DEFAULT_FEATURE_TOGGLES: FeatureToggles = { // ... existing toggles myNewFeature: true, } ``` ### Step 5: (Optional) Add Props Mapping If your component needs props, add it to `getPropsForComponent` in `App.tsx`: ```typescript const getPropsForComponent = (pageId: string) => { const propsMap: Record = { // ... existing mappings 'MyNewDesigner': { data: someData, onDataChange: setSomeData, }, } return propsMap[pageId] || {} } ``` That's it! Your new page will now: - ✅ Appear in the navigation menu - ✅ Be accessible via the keyboard shortcut - ✅ Show/hide based on the feature toggle - ✅ Be searchable in global search - ✅ Follow the same rendering pattern as other pages ## Component Map The `componentMap` in `App.tsx` is the registry of all available components: ```typescript const componentMap: Record> = { ProjectDashboard: lazy(() => import('@/components/ProjectDashboard').then(m => ({ default: m.ProjectDashboard }))), CodeEditor: lazy(() => import('@/components/CodeEditor').then(m => ({ default: m.CodeEditor }))), // ... more components } ``` All components are **lazy-loaded** for optimal performance. They only load when the user navigates to that page. ## Special Layouts ### Split-Pane Layout (Code Editor) The code editor uses a special resizable split-pane layout. This is handled by the `requiresResizable` flag: ```json { "id": "code", "component": "CodeEditor", "requiresResizable": true } ``` The rendering logic in `App.tsx` checks for this flag and renders the appropriate layout. ## Feature Toggles Integration Pages can be conditionally enabled based on feature toggles: ```json { "id": "playwright", "toggleKey": "playwright", "enabled": true } ``` The page will only appear if: 1. `enabled` is `true` in the JSON 2. `featureToggles.playwright` is `true` (or undefined) Users can toggle features on/off in the **Features** page. ## Keyboard Shortcuts Shortcuts are automatically parsed from the configuration: ```json { "shortcut": "ctrl+1" } ``` Supported modifiers: - `ctrl` - Control key - `shift` - Shift key - `alt` - Alt key (not implemented yet, but easy to add) Format: `[modifier+]key` (e.g., "ctrl+1", "ctrl+shift+e", "f") ## Future Enhancements The declarative system can be extended to support: ### 1. Dynamic Props from JSON ```json { "component": "MyComponent", "props": { "title": "Dynamic Title", "showToolbar": true } } ``` ### 2. Layout Configuration ```json { "layout": { "type": "split", "direction": "horizontal", "panels": [ { "component": "Sidebar", "size": 20 }, { "component": "MainContent", "size": 80 } ] } } ``` ### 3. Permission-Based Access ```json { "permissions": ["admin", "editor"] } ``` ### 4. Page Groups/Categories ```json { "category": "Design Tools", "group": "styling" } ``` ### 5. Page Metadata ```json { "description": "Design and test Playwright e2e tests", "tags": ["testing", "automation"], "beta": true } ``` ## Advanced: Page Schema System For even more advanced use cases, check out: - `/src/config/page-schema.ts` - TypeScript types for page schemas - `/src/components/orchestration/PageRenderer.tsx` - Generic page renderer - `/src/config/default-pages.json` - Alternative page configuration format These files provide a more sophisticated schema-based system that can define: - Complex layouts (split, tabs, grid) - Component hierarchies - Action handlers - Context management ## Migration Guide If you have an existing page that's hardcoded in App.tsx: ### Before (Hardcoded): ```typescript }> ``` ### After (Declarative): 1. Add to `pages.json`: ```json { "id": "my-page", "title": "My Page", "icon": "Star", "component": "MyComponent", "enabled": true, "order": 10 } ``` 2. Add to `componentMap`: ```typescript MyComponent: lazy(() => import('@/components/MyComponent').then(m => ({ default: m.MyComponent }))), ``` 3. Add props mapping if needed: ```typescript 'MyComponent': { prop1: data, prop2: handler, } ``` 4. Remove the hardcoded TabsContent - the system handles it automatically! ## Troubleshooting ### Page doesn't appear in navigation - ✅ Check `enabled: true` in pages.json - ✅ Check feature toggle is enabled (if using `toggleKey`) - ✅ Verify component exists in `componentMap` - ✅ Check console for errors ### Component not loading - ✅ Verify import path in `componentMap` - ✅ Check component has a default export - ✅ Look for TypeScript errors in component file ### Keyboard shortcut not working - ✅ Verify shortcut format (e.g., "ctrl+1") - ✅ Check for conflicts with browser shortcuts - ✅ Make sure page is enabled ### Props not being passed - ✅ Add mapping in `getPropsForComponent` - ✅ Verify component name matches `pages.json` - ✅ Check prop types match component interface ## Best Practices 1. **Keep pages.json organized** - Group related pages together, use consistent ordering 2. **Use meaningful IDs** - Use kebab-case, descriptive IDs (e.g., "code-editor", not "ce") 3. **Choose appropriate icons** - Use Phosphor icons that match the page purpose 4. **Document feature toggles** - Add comments to FeatureToggles type 5. **Test shortcuts** - Verify shortcuts don't conflict with browser/OS shortcuts 6. **Lazy load everything** - Keep components in the lazy componentMap 7. **Type your props** - Use TypeScript interfaces for component props 8. **Keep components small** - Follow the <150 LOC guideline from refactoring ## Summary The declarative system transforms CodeForge from a monolithic React app into a flexible, configuration-driven platform. By moving page definitions to JSON, we gain: - 🚀 **Faster development** - Add pages in minutes, not hours - 🔧 **Easier maintenance** - Configuration is centralized and versioned - 📦 **Better performance** - Lazy loading reduces initial bundle size - 🎯 **Cleaner code** - Business logic separated from configuration - 🧪 **Simpler testing** - Mock configuration instead of mocking components The system is designed to grow with your needs - start simple with basic page definitions, then add advanced features like layout configuration, permissions, and dynamic props as needed.