mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-11 14:52:55 +01:00
feat(editor): implement embedded template system (TASK-009)
- Add ProjectTemplate TypeScript interfaces for type-safe templates - Implement EmbeddedTemplateProvider for bundled templates - Create Hello World template (Router + Home page + Text) - Update LocalProjectsModel to use embedded templates by default - Remove programmatic project creation workaround - Fix: Add required fields (id, comments, metadata) per TASK-010 - Fix: Correct node type 'PageRouter' → 'Router' - Add comprehensive developer documentation Benefits: - No more path resolution issues (__dirname/process.cwd()) - Works identically in dev and production - Type-safe template definitions - Easy to add new templates Closes TASK-009 (Phase 3 - Editor UX Overhaul)
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# TASK-009: Template System Refactoring
|
||||
|
||||
**Status**: 📋 Planned
|
||||
**Status**: 🟢 Complete (Backend)
|
||||
**Priority**: Medium
|
||||
**Complexity**: Medium
|
||||
**Estimated Effort**: 2-3 days
|
||||
**Actual Effort**: 1 day (Backend implementation)
|
||||
|
||||
## Context
|
||||
|
||||
@@ -244,12 +244,100 @@ class TemplateManager {
|
||||
- Failed attempt: `packages/noodl-editor/src/editor/src/utils/forge/template/providers/local-template-provider.ts`
|
||||
- Template registry: `packages/noodl-editor/src/editor/src/utils/forge/index.ts`
|
||||
|
||||
## Implementation Summary (January 9, 2026)
|
||||
|
||||
### ✅ What Was Completed
|
||||
|
||||
**Phase 1-3: Backend Implementation (Complete)**
|
||||
|
||||
1. **Type System Created**
|
||||
|
||||
- `ProjectTemplate.ts` - Complete TypeScript interfaces for templates
|
||||
- Comprehensive type definitions for components, nodes, connections, and settings
|
||||
|
||||
2. **EmbeddedTemplateProvider Implemented**
|
||||
|
||||
- Provider that handles `embedded://` protocol
|
||||
- Templates stored as TypeScript objects, bundled by webpack
|
||||
- No file I/O dependencies, works identically in dev and production
|
||||
|
||||
3. **Hello World Template Created**
|
||||
|
||||
- Structure: App → PageRouter → Page "/Home" → Text "Hello World!"
|
||||
- Clean and minimal, demonstrates Page Router usage
|
||||
- Located in `models/template/templates/hello-world.template.ts`
|
||||
|
||||
4. **Template Registry Integration**
|
||||
|
||||
- `EmbeddedTemplateProvider` registered with highest priority
|
||||
- Backward compatible with existing HTTP/Noodl Docs providers
|
||||
|
||||
5. **LocalProjectsModel Updated**
|
||||
|
||||
- Removed programmatic project creation workaround
|
||||
- Default template now uses `embedded://hello-world`
|
||||
- Maintains backward compatibility with external templates
|
||||
|
||||
6. **Documentation**
|
||||
- Complete developer guide in `models/template/README.md`
|
||||
- Instructions for creating custom templates
|
||||
- Architecture overview and best practices
|
||||
|
||||
### 📁 Files Created
|
||||
|
||||
```
|
||||
packages/noodl-editor/src/editor/src/models/template/
|
||||
├── ProjectTemplate.ts # Type definitions
|
||||
├── EmbeddedTemplateProvider.ts # Provider implementation
|
||||
├── README.md # Developer documentation
|
||||
└── templates/
|
||||
└── hello-world.template.ts # Default template
|
||||
```
|
||||
|
||||
### 📝 Files Modified
|
||||
|
||||
- `utils/forge/index.ts` - Registered EmbeddedTemplateProvider
|
||||
- `utils/LocalProjectsModel.ts` - Updated newProject() to use embedded templates
|
||||
|
||||
### 🎯 Benefits Achieved
|
||||
|
||||
✅ No more `__dirname` or `process.cwd()` path resolution issues
|
||||
✅ Templates work identically in development and production builds
|
||||
✅ Type-safe template definitions with full IDE support
|
||||
✅ Easy to add new templates - just create a TypeScript file
|
||||
✅ Maintains backward compatibility with external template URLs
|
||||
|
||||
### ⏳ Remaining Work (Future Tasks)
|
||||
|
||||
- **UI for Template Selection**: Gallery/dialog to choose templates when creating projects
|
||||
- **Additional Templates**: Blank, Dashboard, E-commerce templates
|
||||
- **Template Export**: Allow users to save their projects as templates
|
||||
- **Unit Tests**: Test suite for EmbeddedTemplateProvider
|
||||
- **Template Validation**: Verify template structure before project creation
|
||||
|
||||
### 🚀 Usage
|
||||
|
||||
```typescript
|
||||
// Create project with embedded template (automatic default)
|
||||
LocalProjectsModel.instance.newProject(callback, {
|
||||
name: 'My Project'
|
||||
// Uses 'embedded://hello-world' by default
|
||||
});
|
||||
|
||||
// Create project with specific template
|
||||
LocalProjectsModel.instance.newProject(callback, {
|
||||
name: 'My Project',
|
||||
projectTemplate: 'embedded://hello-world'
|
||||
});
|
||||
```
|
||||
|
||||
## Related Tasks
|
||||
|
||||
- None yet (this is the first comprehensive template system task)
|
||||
- **TASK-009-UI**: Template selection gallery (future)
|
||||
- **TASK-009-EXPORT**: Template export functionality (future)
|
||||
|
||||
---
|
||||
|
||||
**Created**: January 8, 2026
|
||||
**Last Updated**: January 8, 2026
|
||||
**Assignee**: TBD
|
||||
**Last Updated**: January 9, 2026
|
||||
**Implementation**: January 9, 2026 (Backend complete)
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* EmbeddedTemplateProvider
|
||||
*
|
||||
* Provides access to templates that are embedded directly in the application code.
|
||||
* These templates are bundled with the editor and work reliably in both
|
||||
* development and production (no file I/O or path resolution issues).
|
||||
*
|
||||
* @module noodl-editor/models/template
|
||||
*/
|
||||
|
||||
import { ITemplateProvider, TemplateItem } from '../../utils/forge/template/template';
|
||||
import { ProjectTemplate } from './ProjectTemplate';
|
||||
import { helloWorldTemplate } from './templates/hello-world.template';
|
||||
|
||||
/**
|
||||
* Provider for templates that are embedded in the application code
|
||||
*/
|
||||
export class EmbeddedTemplateProvider implements ITemplateProvider {
|
||||
/**
|
||||
* Registry of all embedded templates
|
||||
* New templates should be added here
|
||||
*/
|
||||
private templates: Map<string, ProjectTemplate> = new Map([
|
||||
['hello-world', helloWorldTemplate]
|
||||
// Add more templates here as they are created
|
||||
]);
|
||||
|
||||
get name(): string {
|
||||
return 'embedded-templates';
|
||||
}
|
||||
|
||||
/**
|
||||
* List all available embedded templates
|
||||
* @returns Array of template items
|
||||
*/
|
||||
async list(): Promise<ReadonlyArray<TemplateItem>> {
|
||||
const items: TemplateItem[] = [];
|
||||
|
||||
for (const [id, template] of this.templates) {
|
||||
items.push({
|
||||
title: template.name,
|
||||
desc: template.description,
|
||||
category: template.category,
|
||||
iconURL: template.thumbnail || '',
|
||||
projectURL: `embedded://${id}`,
|
||||
useCloudServices: false,
|
||||
cloudServicesTemplateURL: undefined
|
||||
});
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this provider can handle the given URL
|
||||
* @param url - The template URL to check
|
||||
* @returns True if URL starts with "embedded://"
|
||||
*/
|
||||
async canDownload(url: string): Promise<boolean> {
|
||||
return url.startsWith('embedded://');
|
||||
}
|
||||
|
||||
/**
|
||||
* "Download" (copy) the template to the destination directory
|
||||
*
|
||||
* Note: For embedded templates, we write the project.json directly
|
||||
* rather than copying files from disk.
|
||||
*
|
||||
* @param url - Template URL (e.g., "embedded://hello-world")
|
||||
* @param destination - Destination directory path
|
||||
* @returns Promise that resolves when template is written
|
||||
*/
|
||||
async download(url: string, destination: string): Promise<void> {
|
||||
// Extract template ID from URL
|
||||
const templateId = url.replace('embedded://', '');
|
||||
|
||||
const template = this.templates.get(templateId);
|
||||
if (!template) {
|
||||
throw new Error(`Unknown embedded template: ${templateId}`);
|
||||
}
|
||||
|
||||
// Get the template content (which will have its name overridden by the caller)
|
||||
const projectContent = template.content;
|
||||
|
||||
// Ensure destination directory exists
|
||||
const { filesystem } = await import('@noodl/platform');
|
||||
|
||||
// Create destination directory if it doesn't exist
|
||||
if (!filesystem.exists(destination)) {
|
||||
await filesystem.makeDirectory(destination);
|
||||
}
|
||||
|
||||
// Write project.json to destination
|
||||
const projectJsonPath = filesystem.join(destination, 'project.json');
|
||||
await filesystem.writeFile(projectJsonPath, JSON.stringify(projectContent, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific template by ID (utility method)
|
||||
* @param id - Template ID
|
||||
* @returns The template, or undefined if not found
|
||||
*/
|
||||
getTemplate(id: string): ProjectTemplate | undefined {
|
||||
return this.templates.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all template IDs (utility method)
|
||||
* @returns Array of template IDs
|
||||
*/
|
||||
getTemplateIds(): string[] {
|
||||
return Array.from(this.templates.keys());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* ProjectTemplate
|
||||
*
|
||||
* Defines the structure for project templates that can be used
|
||||
* to create new projects with pre-configured components and settings.
|
||||
*
|
||||
* @module noodl-editor/models/template
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a complete project template structure
|
||||
*/
|
||||
export interface ProjectTemplate {
|
||||
/** Unique identifier for the template */
|
||||
id: string;
|
||||
|
||||
/** Display name of the template */
|
||||
name: string;
|
||||
|
||||
/** Description of what the template provides */
|
||||
description: string;
|
||||
|
||||
/** Category for grouping templates (e.g., "Getting Started", "Dashboard") */
|
||||
category: string;
|
||||
|
||||
/** Template version (semver) */
|
||||
version: string;
|
||||
|
||||
/** Optional thumbnail/icon URL for UI display */
|
||||
thumbnail?: string;
|
||||
|
||||
/** The actual project content */
|
||||
content: ProjectContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* The core content structure of a Noodl project
|
||||
*/
|
||||
export interface ProjectContent {
|
||||
/** Project name (will be overridden by user input) */
|
||||
name: string;
|
||||
|
||||
/** Array of component definitions */
|
||||
components: ComponentDefinition[];
|
||||
|
||||
/** Project-level settings */
|
||||
settings?: ProjectSettings;
|
||||
|
||||
/** Project metadata */
|
||||
metadata?: ProjectMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition of a single component in the project
|
||||
*/
|
||||
export interface ComponentDefinition {
|
||||
/** Component name (e.g., "App", "/#__page__/Home") */
|
||||
name: string;
|
||||
|
||||
/** Component graph structure */
|
||||
graph?: ComponentGraph;
|
||||
|
||||
/** Whether this is a visual component */
|
||||
visual?: boolean;
|
||||
|
||||
/** Component ID (optional, will be generated if not provided) */
|
||||
id?: string;
|
||||
|
||||
/** Port definitions for the component */
|
||||
ports?: PortDefinition[];
|
||||
|
||||
/** Visual state transitions (for visual components) */
|
||||
visualStateTransitions?: unknown[];
|
||||
|
||||
/** Component metadata */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Component graph containing nodes and connections
|
||||
*/
|
||||
export interface ComponentGraph {
|
||||
/** Root nodes in the component */
|
||||
roots: NodeDefinition[];
|
||||
|
||||
/** Connections between nodes */
|
||||
connections: ConnectionDefinition[];
|
||||
|
||||
/** Comments in the graph (required by NodeGraphModel) */
|
||||
comments?: unknown[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition of a single node in the component graph
|
||||
*/
|
||||
export interface NodeDefinition {
|
||||
/** Unique node ID */
|
||||
id: string;
|
||||
|
||||
/** Node type (e.g., "Group", "Text", "PageRouter") */
|
||||
type: string;
|
||||
|
||||
/** X position on canvas */
|
||||
x: number;
|
||||
|
||||
/** Y position on canvas */
|
||||
y: number;
|
||||
|
||||
/** Node parameters/properties */
|
||||
parameters: Record<string, unknown>;
|
||||
|
||||
/** Port definitions */
|
||||
ports?: PortDefinition[];
|
||||
|
||||
/** Child nodes (for visual hierarchy) */
|
||||
children?: NodeDefinition[];
|
||||
|
||||
/** Variant (for some node types) */
|
||||
variant?: string;
|
||||
|
||||
/** State parameters (for state nodes) */
|
||||
stateParameters?: Record<string, unknown>;
|
||||
|
||||
/** State transitions (for state nodes) */
|
||||
stateTransitions?: unknown[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection between two nodes
|
||||
*/
|
||||
export interface ConnectionDefinition {
|
||||
/** Source node ID */
|
||||
fromId: string;
|
||||
|
||||
/** Source port/property name */
|
||||
fromProperty: string;
|
||||
|
||||
/** Target node ID */
|
||||
toId: string;
|
||||
|
||||
/** Target port/property name */
|
||||
toProperty: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Port definition for components/nodes
|
||||
*/
|
||||
export interface PortDefinition {
|
||||
/** Port name */
|
||||
name: string;
|
||||
|
||||
/** Port type (e.g., "string", "number", "signal") */
|
||||
type: string;
|
||||
|
||||
/** Port direction ("input" or "output") */
|
||||
plug: 'input' | 'output';
|
||||
|
||||
/** Port index (for ordering) */
|
||||
index?: number;
|
||||
|
||||
/** Default value */
|
||||
default?: unknown;
|
||||
|
||||
/** Display name */
|
||||
displayName?: string;
|
||||
|
||||
/** Port group */
|
||||
group?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Project-level settings
|
||||
*/
|
||||
export interface ProjectSettings {
|
||||
/** Project settings go here */
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Project metadata
|
||||
*/
|
||||
export interface ProjectMetadata {
|
||||
/** Project title */
|
||||
title?: string;
|
||||
|
||||
/** Project description */
|
||||
description?: string;
|
||||
|
||||
/** Other metadata fields */
|
||||
[key: string]: unknown;
|
||||
}
|
||||
173
packages/noodl-editor/src/editor/src/models/template/README.md
Normal file
173
packages/noodl-editor/src/editor/src/models/template/README.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Template System Documentation
|
||||
|
||||
This directory contains the embedded project template system implemented in TASK-009.
|
||||
|
||||
## Overview
|
||||
|
||||
The template system allows creating new Noodl projects from pre-defined templates that are embedded directly in the application code. This ensures templates work reliably in both development and production without file path resolution issues.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
models/template/
|
||||
├── ProjectTemplate.ts # TypeScript interfaces for templates
|
||||
├── EmbeddedTemplateProvider.ts # Provider for embedded templates
|
||||
├── templates/ # Template definitions
|
||||
│ └── hello-world.template.ts # Default Hello World template
|
||||
└── README.md # This file
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
1. **Template Definition**: Templates are defined as TypeScript objects using the `ProjectTemplate` interface
|
||||
2. **Provider Registration**: The `EmbeddedTemplateProvider` is registered in `utils/forge/index.ts` with the highest priority
|
||||
3. **Template Usage**: When creating a new project, templates are referenced via `embedded://template-id` URLs
|
||||
4. **Project Creation**: The provider writes the template's `project.json` directly to the destination directory
|
||||
|
||||
## Creating a New Template
|
||||
|
||||
### Step 1: Define Your Template
|
||||
|
||||
Create a new file in `templates/` (e.g., `dashboard.template.ts`):
|
||||
|
||||
```typescript
|
||||
import { ProjectTemplate } from '../ProjectTemplate';
|
||||
|
||||
export const dashboardTemplate: ProjectTemplate = {
|
||||
id: 'dashboard',
|
||||
name: 'Dashboard Template',
|
||||
description: 'A dashboard with navigation and multiple pages',
|
||||
category: 'Business Apps',
|
||||
version: '1.0.0',
|
||||
thumbnail: undefined,
|
||||
|
||||
content: {
|
||||
name: 'Dashboard Project',
|
||||
components: [
|
||||
// Define your components here
|
||||
{
|
||||
name: 'App',
|
||||
visual: true,
|
||||
ports: [],
|
||||
visualStateTransitions: [],
|
||||
graph: {
|
||||
roots: [
|
||||
// Add your nodes here
|
||||
],
|
||||
connections: []
|
||||
}
|
||||
}
|
||||
],
|
||||
settings: {},
|
||||
metadata: {
|
||||
title: 'Dashboard Project',
|
||||
description: 'A complete dashboard template'
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Step 2: Register the Template
|
||||
|
||||
Add your template to `EmbeddedTemplateProvider.ts`:
|
||||
|
||||
```typescript
|
||||
import { dashboardTemplate } from './templates/dashboard.template';
|
||||
|
||||
export class EmbeddedTemplateProvider implements ITemplateProvider {
|
||||
private templates: Map<string, ProjectTemplate> = new Map([
|
||||
['hello-world', helloWorldTemplate],
|
||||
['dashboard', dashboardTemplate] // Add your template here
|
||||
]);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Use Your Template
|
||||
|
||||
Create a project with your template:
|
||||
|
||||
```typescript
|
||||
LocalProjectsModel.instance.newProject(
|
||||
(project) => {
|
||||
console.log('Project created:', project);
|
||||
},
|
||||
{
|
||||
name: 'My Dashboard',
|
||||
projectTemplate: 'embedded://dashboard'
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
## Template Structure Reference
|
||||
|
||||
### Component Definition
|
||||
|
||||
```typescript
|
||||
{
|
||||
name: 'ComponentName', // Component name (use '/#__page__/Name' for pages)
|
||||
visual: true, // Whether this is a visual component
|
||||
ports: [], // Component ports
|
||||
visualStateTransitions: [], // State transitions
|
||||
graph: {
|
||||
roots: [/* nodes */], // Root-level nodes
|
||||
connections: [] // Connections between nodes
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Node Definition
|
||||
|
||||
```typescript
|
||||
{
|
||||
id: generateId(), // Unique node ID
|
||||
type: 'NodeType', // Node type (e.g., 'Text', 'Group', 'PageRouter')
|
||||
x: 100, // X position on canvas
|
||||
y: 100, // Y position on canvas
|
||||
parameters: { // Node parameters
|
||||
text: 'Hello',
|
||||
fontSize: { value: 16, unit: 'px' }
|
||||
},
|
||||
ports: [], // Node-specific ports
|
||||
children: [] // Child nodes (for visual hierarchy)
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Use the Helper Function**: Use the `generateId()` function for generating unique IDs
|
||||
2. **Structure Over Data**: Define component structure, not specific user data
|
||||
3. **Minimal & Clear**: Keep templates simple and focused on structure
|
||||
4. **Test Both Modes**: Test templates in both development and production builds
|
||||
5. **Document Purpose**: Add JSDoc comments explaining what the template provides
|
||||
|
||||
## Default Template
|
||||
|
||||
When no template is specified in `newProject()`, the system automatically uses `embedded://hello-world` as the default template.
|
||||
|
||||
## Advantages Over Previous System
|
||||
|
||||
✅ **No Path Resolution Issues**: Templates are embedded in code, bundled by webpack
|
||||
✅ **Dev/Prod Parity**: Works identically in development and production
|
||||
✅ **Type Safety**: Full TypeScript support with interfaces
|
||||
✅ **Easy to Extend**: Add new templates by creating a file and registering it
|
||||
✅ **No External Dependencies**: No need for external template files or URLs
|
||||
|
||||
## Migration from Old System
|
||||
|
||||
The old system used:
|
||||
|
||||
- Programmatic project creation (JSON literal in code)
|
||||
- File-based templates (with path resolution issues)
|
||||
- External template URLs
|
||||
|
||||
The new system:
|
||||
|
||||
- Uses embedded template objects
|
||||
- Provides a consistent API via `templateRegistry`
|
||||
- Maintains backward compatibility with external template URLs
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: January 9, 2026
|
||||
**Related**: TASK-009-template-system-refactoring
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Hello World Template
|
||||
*
|
||||
* A simple starter project with:
|
||||
* - App component (root)
|
||||
* - Page Router configured
|
||||
* - Home page with "Hello World" text
|
||||
*
|
||||
* @module noodl-editor/models/template/templates
|
||||
*/
|
||||
|
||||
import { ProjectTemplate } from '../ProjectTemplate';
|
||||
|
||||
/**
|
||||
* Generate a unique ID for nodes
|
||||
*/
|
||||
function generateId(): string {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = (Math.random() * 16) | 0;
|
||||
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hello World template
|
||||
* Creates a basic project with Page Router and a home page
|
||||
*/
|
||||
export const helloWorldTemplate: ProjectTemplate = {
|
||||
id: 'hello-world',
|
||||
name: 'Hello World',
|
||||
description: 'A simple starter project with a home page displaying "Hello World"',
|
||||
category: 'Getting Started',
|
||||
version: '1.0.0',
|
||||
thumbnail: undefined,
|
||||
|
||||
content: {
|
||||
name: 'Hello World Project',
|
||||
components: [
|
||||
// App component (root)
|
||||
{
|
||||
name: 'App',
|
||||
id: generateId(),
|
||||
visual: true,
|
||||
ports: [],
|
||||
visualStateTransitions: [],
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: generateId(),
|
||||
type: 'Router',
|
||||
x: 100,
|
||||
y: 100,
|
||||
parameters: {
|
||||
startPage: '/#__page__/Home'
|
||||
},
|
||||
ports: [],
|
||||
children: []
|
||||
}
|
||||
],
|
||||
connections: [],
|
||||
comments: []
|
||||
},
|
||||
metadata: {}
|
||||
},
|
||||
// Home Page component
|
||||
{
|
||||
name: '/#__page__/Home',
|
||||
id: generateId(),
|
||||
visual: true,
|
||||
ports: [],
|
||||
visualStateTransitions: [],
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: generateId(),
|
||||
type: 'Text',
|
||||
x: 100,
|
||||
y: 100,
|
||||
parameters: {
|
||||
text: 'Hello World!',
|
||||
fontSize: { value: 32, unit: 'px' },
|
||||
textAlign: 'center'
|
||||
},
|
||||
ports: [],
|
||||
children: []
|
||||
}
|
||||
],
|
||||
connections: [],
|
||||
comments: []
|
||||
},
|
||||
metadata: {}
|
||||
}
|
||||
],
|
||||
settings: {},
|
||||
metadata: {
|
||||
title: 'Hello World Project',
|
||||
description: 'A simple starter project'
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -260,57 +260,21 @@ export class LocalProjectsModel extends Model {
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Create a minimal Hello World project programmatically
|
||||
// This is a temporary solution until TASK-009-template-system-refactoring is implemented
|
||||
const minimalProject = {
|
||||
name: name,
|
||||
components: [
|
||||
{
|
||||
name: 'App',
|
||||
id: guid(),
|
||||
graph: {
|
||||
roots: [
|
||||
{
|
||||
id: guid(),
|
||||
type: 'Group',
|
||||
x: 0,
|
||||
y: 0,
|
||||
parameters: {},
|
||||
ports: [],
|
||||
children: [
|
||||
{
|
||||
id: guid(),
|
||||
type: 'Text',
|
||||
x: 50,
|
||||
y: 50,
|
||||
parameters: {
|
||||
text: 'Hello World!'
|
||||
},
|
||||
ports: [],
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
connections: [],
|
||||
comments: []
|
||||
},
|
||||
metadata: {}
|
||||
}
|
||||
],
|
||||
settings: {},
|
||||
metadata: {
|
||||
title: name,
|
||||
description: 'A new Noodl project'
|
||||
}
|
||||
};
|
||||
// No template specified - use default embedded Hello World template
|
||||
// This uses the template system implemented in TASK-009
|
||||
const defaultTemplate = 'embedded://hello-world';
|
||||
|
||||
await filesystem.writeFile(filesystem.join(dirEntry, 'project.json'), JSON.stringify(minimalProject, null, 2));
|
||||
// For embedded templates, write directly to the project directory
|
||||
// (no need for temporary folder + copy)
|
||||
const { EmbeddedTemplateProvider } = await import('../models/template/EmbeddedTemplateProvider');
|
||||
const embeddedProvider = new EmbeddedTemplateProvider();
|
||||
|
||||
await embeddedProvider.download(defaultTemplate, dirEntry);
|
||||
|
||||
// Load the newly created project
|
||||
projectFromDirectory(dirEntry, (project) => {
|
||||
if (!project) {
|
||||
console.error('Failed to create project from generated structure');
|
||||
console.error('Failed to create project from template');
|
||||
fn();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import getDocsEndpoint from '@noodl-utils/getDocsEndpoint';
|
||||
|
||||
import { EmbeddedTemplateProvider } from '../../models/template/EmbeddedTemplateProvider';
|
||||
import { HttpTemplateProvider } from './template/providers/http-template-provider';
|
||||
import { NoodlDocsTemplateProvider } from './template/providers/noodl-docs-template-provider';
|
||||
import { TemplateRegistry } from './template/template-registry';
|
||||
|
||||
// The order of the providers matters,
|
||||
// when looking for a template it will take the first one that allows it.
|
||||
// EmbeddedTemplateProvider is first as it provides built-in templates that work reliably.
|
||||
const templateRegistry = new TemplateRegistry([
|
||||
new EmbeddedTemplateProvider(),
|
||||
new NoodlDocsTemplateProvider(getDocsEndpoint),
|
||||
new HttpTemplateProvider()
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user