Files
OpenNoodl/dev-docs/tasks/phase-3/TASK-003-shared-component-system/COMP-002-builtin-prefabs.md

395 lines
11 KiB
Markdown

# COMP-002: Built-in Prefabs
## Overview
Bundle essential prefabs directly with the OpenNoodl editor, so they're available immediately without network access. This improves the onboarding experience and ensures core functionality is always available.
## Context
Currently, all prefabs are fetched from the docs endpoint at runtime:
- Requires network connectivity
- Adds latency on first load
- No prefabs available offline
- New users see empty prefab library initially
By bundling prefabs with the editor:
- Instant availability
- Works offline
- Consistent experience for all users
- Core prefabs versioned with editor releases
### Existing Export/Import
From `exportProjectComponents.ts` and `projectimporter.js`:
- Components exported as zip files
- Import handles collision detection
- Styles, variants, resources included
- Dependency tracking exists
## Requirements
### Functional Requirements
1. **Built-in Prefab Bundle**
- Essential prefabs bundled in editor distribution
- Loaded from local filesystem, not network
- Versioned with editor releases
2. **Prefab Selection**
- Form components (Input, Button, Checkbox, etc.)
- Layout helpers (Card, Modal, Drawer)
- Data utilities (REST caller, LocalStorage, etc.)
- Authentication flows (basic patterns)
- Navigation patterns
3. **UI Distinction**
- "Built-in" badge on bundled prefabs
- Shown first in prefab list
- Separate section or filter option
4. **Update Mechanism**
- Built-in prefabs update with editor
- No manual update needed
- Changelog visible for what's new
5. **Offline First**
- Available immediately on fresh install
- No network request needed
- Graceful handling when docs unavailable
### Non-Functional Requirements
- Bundle size impact < 5MB
- Load time < 500ms
- No runtime network dependency
- Works in air-gapped environments
## Technical Approach
### 1. Bundle Structure
```
packages/noodl-editor/
├── static/
│ └── builtin-prefabs/
│ ├── index.json # Manifest of built-in prefabs
│ └── prefabs/
│ ├── form-input/
│ │ ├── prefab.json # Metadata
│ │ └── components/ # Component files
│ ├── form-button/
│ ├── card-layout/
│ ├── modal-dialog/
│ ├── rest-client/
│ └── ...
```
### 2. Manifest Format
```json
{
"version": "1.0.0",
"noodlVersion": "2.10.0",
"prefabs": [
{
"id": "builtin:form-input",
"name": "Form Input",
"description": "Styled text input with label, validation, and error states",
"version": "1.0.0",
"category": "Forms",
"tags": ["form", "input", "text", "validation"],
"icon": "input-icon.svg",
"path": "prefabs/form-input"
}
]
}
```
### 3. BuiltInPrefabSource Implementation
```typescript
// packages/noodl-editor/src/editor/src/models/prefab/sources/BuiltInPrefabSource.ts
import { platform } from '@noodl/platform';
class BuiltInPrefabSource implements PrefabSource {
config = {
id: 'builtin',
name: 'Built-in',
priority: 100, // Highest priority - show first
enabled: true
};
private manifest: BuiltInManifest | null = null;
private basePath: string;
async initialize(): Promise<void> {
// Get path to bundled prefabs
this.basePath = platform.getBuiltInPrefabsPath();
// Load manifest
const manifestPath = path.join(this.basePath, 'index.json');
const content = await fs.readFile(manifestPath, 'utf-8');
this.manifest = JSON.parse(content);
}
async listPrefabs(): Promise<PrefabMetadata[]> {
if (!this.manifest) await this.initialize();
return this.manifest.prefabs.map(p => ({
id: p.id,
name: p.name,
description: p.description,
version: p.version,
tags: p.tags,
icon: this.resolveIcon(p.icon),
source: 'builtin',
category: p.category
}));
}
async downloadPrefab(id: string): Promise<string> {
// No download needed - return local path
const prefab = this.manifest.prefabs.find(p => p.id === id);
return path.join(this.basePath, prefab.path);
}
private resolveIcon(iconPath: string): string {
return `file://${path.join(this.basePath, 'icons', iconPath)}`;
}
}
```
### 4. Build-time Prefab Bundling
```typescript
// scripts/bundle-prefabs.ts
/**
* Run during build to prepare built-in prefabs
* 1. Reads prefab source projects
* 2. Exports components
* 3. Generates manifest
* 4. Copies to static directory
*/
async function bundlePrefabs() {
const prefabSources = await glob('prefab-sources/*');
const manifest: BuiltInManifest = {
version: packageJson.version,
noodlVersion: packageJson.version,
prefabs: []
};
for (const source of prefabSources) {
const metadata = await readPrefabMetadata(source);
const outputPath = path.join(OUTPUT_DIR, metadata.id);
await exportPrefabComponents(source, outputPath);
manifest.prefabs.push({
id: `builtin:${metadata.id}`,
name: metadata.name,
description: metadata.description,
version: metadata.version,
category: metadata.category,
tags: metadata.tags,
icon: metadata.icon,
path: metadata.id
});
}
await writeManifest(manifest);
}
```
### 5. Prefab Categories
```typescript
enum PrefabCategory {
Forms = 'Forms',
Layout = 'Layout',
Navigation = 'Navigation',
Data = 'Data',
Authentication = 'Authentication',
Feedback = 'Feedback',
Media = 'Media'
}
const BUILT_IN_PREFABS: BuiltInPrefabConfig[] = [
// Forms
{ id: 'form-input', category: PrefabCategory.Forms },
{ id: 'form-textarea', category: PrefabCategory.Forms },
{ id: 'form-checkbox', category: PrefabCategory.Forms },
{ id: 'form-radio', category: PrefabCategory.Forms },
{ id: 'form-select', category: PrefabCategory.Forms },
{ id: 'form-button', category: PrefabCategory.Forms },
// Layout
{ id: 'card', category: PrefabCategory.Layout },
{ id: 'modal', category: PrefabCategory.Layout },
{ id: 'drawer', category: PrefabCategory.Layout },
{ id: 'accordion', category: PrefabCategory.Layout },
{ id: 'tabs', category: PrefabCategory.Layout },
// Navigation
{ id: 'navbar', category: PrefabCategory.Navigation },
{ id: 'sidebar', category: PrefabCategory.Navigation },
{ id: 'breadcrumb', category: PrefabCategory.Navigation },
{ id: 'pagination', category: PrefabCategory.Navigation },
// Data
{ id: 'rest-client', category: PrefabCategory.Data },
{ id: 'local-storage', category: PrefabCategory.Data },
{ id: 'data-table', category: PrefabCategory.Data },
// Feedback
{ id: 'toast', category: PrefabCategory.Feedback },
{ id: 'loading-spinner', category: PrefabCategory.Feedback },
{ id: 'progress-bar', category: PrefabCategory.Feedback },
];
```
## Files to Create
1. `packages/noodl-editor/static/builtin-prefabs/index.json` - Manifest
2. `packages/noodl-editor/static/builtin-prefabs/prefabs/` - Prefab directories
3. `packages/noodl-editor/src/editor/src/models/prefab/sources/BuiltInPrefabSource.ts` - Source implementation
4. `scripts/bundle-prefabs.ts` - Build script
5. `prefab-sources/` - Source projects for built-in prefabs
## Files to Modify
1. `packages/noodl-editor/src/editor/src/models/prefab/PrefabRegistry.ts`
- Register BuiltInPrefabSource
- Add category support
2. `packages/noodl-editor/src/editor/src/views/NodePicker/tabs/NodePickerSearchView/NodePickerSearchView.tsx`
- Add category filtering
- Show "Built-in" badge
3. `packages/noodl-editor/src/editor/src/views/NodePicker/components/ModuleCard/ModuleCard.tsx`
- Add "Built-in" badge styling
- Show category
4. `package.json`
- Add bundle-prefabs script
5. `webpack.config.js` or equivalent
- Include static/builtin-prefabs in build
## Implementation Steps
### Phase 1: Infrastructure
1. Create bundle directory structure
2. Implement BuiltInPrefabSource
3. Create manifest format
4. Register source in PrefabRegistry
### Phase 2: Build Pipeline
1. Create bundle-prefabs script
2. Add to build process
3. Test bundling works
### Phase 3: Initial Prefabs
1. Create Form Input prefab
2. Create Form Button prefab
3. Create Card layout prefab
4. Test import/collision handling
### Phase 4: UI Updates
1. Add "Built-in" badge
2. Add category filter
3. Show built-in prefabs first
### Phase 5: Full Prefab Set
1. Create remaining form prefabs
2. Create layout prefabs
3. Create data prefabs
4. Create navigation prefabs
### Phase 6: Documentation
1. Document built-in prefabs
2. Add usage examples
3. Create component docs
## Initial Built-in Prefabs
### Priority 1 (MVP)
| Prefab | Category | Components |
|--------|----------|------------|
| Form Input | Forms | TextInput, Label, ErrorMessage |
| Form Button | Forms | Button, LoadingState |
| Card | Layout | Card, CardHeader, CardBody |
| Modal | Layout | Modal, ModalTrigger, ModalContent |
| REST Client | Data | RESTRequest, ResponseHandler |
### Priority 2
| Prefab | Category | Components |
|--------|----------|------------|
| Form Textarea | Forms | Textarea, CharCount |
| Form Checkbox | Forms | Checkbox, CheckboxGroup |
| Form Select | Forms | Select, Option |
| Drawer | Layout | Drawer, DrawerTrigger |
| Toast | Feedback | Toast, ToastContainer |
### Priority 3
| Prefab | Category | Components |
|--------|----------|------------|
| Tabs | Layout | TabBar, TabPanel |
| Accordion | Layout | Accordion, AccordionItem |
| Navbar | Navigation | Navbar, NavItem |
| Data Table | Data | Table, Column, Row, Cell |
## Testing Checklist
- [ ] Built-in prefabs load without network
- [ ] Prefabs appear first in list
- [ ] "Built-in" badge displays correctly
- [ ] Category filter works
- [ ] Import works for each prefab
- [ ] Collision detection works
- [ ] Styles import correctly
- [ ] Works in air-gapped environment
- [ ] Bundle size is acceptable
- [ ] Load time is acceptable
## Dependencies
- COMP-001 (Prefab System Refactoring)
## Blocked By
- COMP-001
## Blocks
- None (can proceed in parallel with COMP-003+)
## Estimated Effort
- Infrastructure: 3-4 hours
- Build pipeline: 2-3 hours
- BuiltInPrefabSource: 2-3 hours
- MVP prefabs (5): 8-10 hours
- UI updates: 2-3 hours
- Testing: 2-3 hours
- **Total: 19-26 hours**
## Success Criteria
1. Built-in prefabs available immediately
2. Work offline without network
3. Clear "Built-in" distinction in UI
4. Categories organize prefabs logically
5. Import flow works smoothly
6. Bundle size < 5MB
## Future Enhancements
- User can hide built-in prefabs
- Community voting for built-in inclusion
- Per-category enable/disable
- Built-in prefab updates notification
- Prefab source code viewing