working on problem opening projet

This commit is contained in:
Tara West
2026-01-12 13:27:19 +01:00
parent c1cc4b9b98
commit 188d993420
18 changed files with 2151 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
/**
* Default Style Tokens (Minimal Set for MVP)
*
* This file defines the minimal set of CSS custom properties (design tokens)
* that will be available in every Noodl project.
*
* These tokens can be used in any CSS property that accepts the relevant value type.
* Example: style="background: var(--primary); padding: var(--space-md);"
*
* @module StyleTokens
*/
export interface StyleToken {
name: string;
value: string;
category: TokenCategory;
description: string;
}
export type TokenCategory = 'color' | 'spacing' | 'border' | 'shadow';
/**
* Minimal set of design tokens for MVP
* Following modern design system conventions (similar to Tailwind/shadcn)
*/
export const DEFAULT_TOKENS: Record<string, StyleToken> = {
// ===== COLORS =====
'--primary': {
name: '--primary',
value: '#3b82f6', // Blue
category: 'color',
description: 'Primary brand color for main actions and highlights'
},
'--background': {
name: '--background',
value: '#ffffff',
category: 'color',
description: 'Main background color'
},
'--foreground': {
name: '--foreground',
value: '#0f172a', // Near black
category: 'color',
description: 'Main text color'
},
'--border': {
name: '--border',
value: '#e2e8f0', // Light gray
category: 'color',
description: 'Default border color'
},
// ===== SPACING =====
'--space-sm': {
name: '--space-sm',
value: '8px',
category: 'spacing',
description: 'Small spacing (padding, margin, gap)'
},
'--space-md': {
name: '--space-md',
value: '16px',
category: 'spacing',
description: 'Medium spacing (padding, margin, gap)'
},
'--space-lg': {
name: '--space-lg',
value: '24px',
category: 'spacing',
description: 'Large spacing (padding, margin, gap)'
},
// ===== BORDERS =====
'--radius-md': {
name: '--radius-md',
value: '8px',
category: 'border',
description: 'Medium border radius for rounded corners'
},
// ===== SHADOWS =====
'--shadow-sm': {
name: '--shadow-sm',
value: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
category: 'shadow',
description: 'Small shadow for subtle elevation'
},
'--shadow-md': {
name: '--shadow-md',
value: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
category: 'shadow',
description: 'Medium shadow for moderate elevation'
}
};
/**
* Get all default tokens as a simple key-value map
* Useful for CSS injection
*/
export function getDefaultTokenValues(): Record<string, string> {
const values: Record<string, string> = {};
for (const [key, token] of Object.entries(DEFAULT_TOKENS)) {
values[key] = token.value;
}
return values;
}
/**
* Get tokens by category
*/
export function getTokensByCategory(category: TokenCategory): StyleToken[] {
return Object.values(DEFAULT_TOKENS).filter((token) => token.category === category);
}

View File

@@ -0,0 +1,211 @@
/**
* Style Tokens Model
*
* Manages CSS custom properties (design tokens) for a Noodl project.
* Tokens are stored in project metadata and can be customized per project.
*
* @module StyleTokens
*/
import Model from '../../../../shared/model';
import { EventDispatcher } from '../../../../shared/utils/EventDispatcher';
import { ProjectModel } from '../projectmodel';
import { getDefaultTokenValues, DEFAULT_TOKENS, StyleToken, TokenCategory } from './DefaultTokens';
export class StyleTokensModel extends Model {
/** Custom token values (overrides defaults) */
private customTokens: Record<string, string>;
constructor() {
super();
this.customTokens = {};
this.loadFromProject();
this.bindListeners();
}
/**
* Bind to project events to stay in sync
*/
private bindListeners() {
const onProjectChanged = () => {
this.loadFromProject();
this.notifyListeners('tokensChanged');
};
EventDispatcher.instance.on(
['ProjectModel.importComplete', 'ProjectModel.instanceHasChanged'],
() => {
if (ProjectModel.instance) {
onProjectChanged();
}
},
this
);
EventDispatcher.instance.on(
'ProjectModel.metadataChanged',
({ key }) => {
if (key === 'styleTokens') {
onProjectChanged();
}
},
this
);
}
/**
* Unbind listeners
*/
private unbindListeners() {
EventDispatcher.instance.off(this);
}
/**
* Load tokens from current project
*/
private loadFromProject() {
if (ProjectModel.instance) {
this.customTokens = ProjectModel.instance.getMetaData('styleTokens') || {};
} else {
this.customTokens = {};
}
}
/**
* Save tokens to current project
*/
private saveToProject() {
if (ProjectModel.instance) {
this.unbindListeners();
ProjectModel.instance.setMetaData('styleTokens', this.customTokens);
this.bindListeners();
}
}
/**
* Get all tokens (defaults + custom overrides)
*/
getAllTokens(): Record<string, string> {
const defaults = getDefaultTokenValues();
return {
...defaults,
...this.customTokens
};
}
/**
* Get a specific token value
* @param name Token name (e.g., '--primary')
* @returns Token value or undefined if not found
*/
getToken(name: string): string | undefined {
// Check custom tokens first
if (this.customTokens[name] !== undefined) {
return this.customTokens[name];
}
// Fall back to default
const defaultToken = DEFAULT_TOKENS[name];
return defaultToken?.value;
}
/**
* Set a custom token value
* @param name Token name (e.g., '--primary')
* @param value Token value (e.g., '#ff0000')
*/
setToken(name: string, value: string) {
// Validate token name starts with --
if (!name.startsWith('--')) {
console.warn(`Token name must start with -- : ${name}`);
return;
}
this.customTokens[name] = value;
this.saveToProject();
this.notifyListeners('tokensChanged');
this.notifyListeners('tokenChanged', { name, value });
}
/**
* Reset a token to its default value
* @param name Token name (e.g., '--primary')
*/
resetToken(name: string) {
if (this.customTokens[name] !== undefined) {
delete this.customTokens[name];
this.saveToProject();
this.notifyListeners('tokensChanged');
this.notifyListeners('tokenReset', { name });
}
}
/**
* Reset all tokens to defaults
*/
resetAllTokens() {
this.customTokens = {};
this.saveToProject();
this.notifyListeners('tokensChanged');
this.notifyListeners('allTokensReset');
}
/**
* Check if a token has been customized
*/
isTokenCustomized(name: string): boolean {
return this.customTokens[name] !== undefined;
}
/**
* Get token metadata
*/
getTokenInfo(name: string): StyleToken | undefined {
return DEFAULT_TOKENS[name];
}
/**
* Get all tokens by category
*/
getTokensByCategory(category: TokenCategory): Record<string, string> {
const tokens: Record<string, string> = {};
for (const [name, tokenInfo] of Object.entries(DEFAULT_TOKENS)) {
if (tokenInfo.category === category) {
tokens[name] = this.getToken(name) || tokenInfo.value;
}
}
return tokens;
}
/**
* Generate CSS string for injection
* @returns CSS custom properties as a string
*/
generateCSS(): string {
const allTokens = this.getAllTokens();
const entries = Object.entries(allTokens);
if (entries.length === 0) {
return '';
}
const declarations = entries.map(([name, value]) => ` ${name}: ${value};`).join('\n');
return `:root {\n${declarations}\n}`;
}
/**
* Cleanup
*/
dispose() {
this.unbindListeners();
this.removeAllListeners();
}
}
/**
* Singleton instance
*/
export const StyleTokens = new StyleTokensModel();

View File

@@ -0,0 +1,11 @@
/**
* Style Tokens System
*
* Exports for the Style Tokens system
*
* @module StyleTokens
*/
export { StyleTokensModel, StyleTokens } from './StyleTokensModel';
export { DEFAULT_TOKENS, getDefaultTokenValues, getTokensByCategory } from './DefaultTokens';
export type { StyleToken, TokenCategory } from './DefaultTokens';

View File

@@ -169,6 +169,14 @@ export class ProjectModel extends Model {
if (json.rootNodeId) _this.rootNode = _this.findNodeWithId(json.rootNodeId);
// Handle rootComponent from templates (name of component instead of node ID)
if (json.rootComponent && !_this.rootNode) {
const rootComponent = _this.getComponentWithName(json.rootComponent);
if (rootComponent) {
_this.setRootComponent(rootComponent);
}
}
// Upgrade project if necessary
ProjectModel.upgrade(_this);

View File

@@ -40,6 +40,9 @@ export interface ProjectContent {
/** Project name (will be overridden by user input) */
name: string;
/** Name of the root component that serves as the entry point */
rootComponent?: string;
/** Array of component definitions */
components: ComponentDefinition[];

View File

@@ -36,6 +36,7 @@ export const helloWorldTemplate: ProjectTemplate = {
content: {
name: 'Hello World Project',
rootComponent: 'App',
components: [
// App component (root)
{