Smart Property Integration
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
🧠 Smart Property Integration: Backend ↔ Frontend
Session: 10
Date: December 10, 2025
Agent: AI Analysis & Architecture
Confidence: 95%
🎯 Executive Summary
Current State: We have TWO property systems that don't talk to each other:
- Backend: Rich MongoDB property system with templates, validation, AI context
- Frontend: Smart property factories with mappers and embeddable definitions
Goal: Create a unified, intelligent property system where:
- Backend properties are the single source of truth
- Frontend smart properties consume backend definitions
- Widgets/embeddables reference backend property IDs (MongoDB-ready)
- AI can generate new properties and understand existing ones
- Variant creation becomes trivial (just property value overrides)
📊 Current Architecture Analysis
Backend Property System (MongoDB)
Collection: gograb-core.core_properties
interface PropertyDefinition {
id: string; // "core_label", "core_backgroundColor"
key: string; // "label", "backgroundColor"
label: string; // "Label", "Background Color"
description: string;
controlType: PropertyControlType; // "text" | "color" | "select" | ...
category: PropertyCategory; // "content" | "appearance" | "layout" | ...
defaultValue: unknown;
validation?: PropertyValidation;
widgetTypes?: string[]; // Which widgets can use this
tags: string[];
themeVariable?: string; // "--ux-surface", "--ux-primary"
aiContext?: string; // Gold for AI training!
metadata: {
created: string;
modified: string;
version: string;
usageCount: number;
isBuiltIn: boolean;
};
}
Key Features:
- ✅ Theme variable integration (
--ux-surface,--ux-primary) - ✅ AI context for training ("Use theme variables: var(--ux-primary)...")
- ✅ Validation with options (dropdowns, min/max, etc.)
- ✅ Usage tracking (know which properties are popular)
- ✅ Widget type filtering (some properties only for buttons, inputs, etc.)
Property Templates (from screenshot):
template_spacing: Margin and padding propertiestemplate_colors: Background and foreground colorstemplate_typography: Font size, weight, transform
Frontend Property System
Location: @libs-foundational/core-properties
// Smart property factories
export const SmartButtonProperties = {
variant: (overrides) => createSelectProperty({
name: 'variant',
defaultValue: 'primary',
options: [
{ value: 'primary', label: 'Primary (Solid)' },
{ value: 'secondary', label: 'Secondary (Outline)' },
// ...
],
transform: mapButtonVariant, // Maps to CSS classes
}),
// ...
};
Key Features:
- ✅ Mappers for value transformation (
mapButtonVariant,mapSize) - ✅ Factory pattern for reusability
- ✅ TypeScript type safety
- ❌ NOT connected to backend
- ❌ Duplicates property definitions
- ❌ No AI context metadata
Embeddable Definitions
Location: embeddables/embeddable-schemas/src/lib/ui-core/button.embeddable.ts
export const BUTTON_EMBEDDABLE = {
id: 'ui-button',
inputs: [
{
name: 'text',
type: 'string',
defaultValue: 'Button',
group: 'content',
// ❌ Hardcoded - not referencing backend property
},
SmartButtonProperties.variant({ order: 1 }),
// ...
],
};
Issues:
- ❌ Properties defined inline OR via smart factories
- ❌ No backend property ID references
- ❌ Can't sync with MongoDB property updates
- ❌ Duplicates metadata that exists in backend
🚨 The Core Problem
┌─────────────────────────────────────────────────────────────┐
│ DISCONNECT │
├─────────────────────────────────────────────────────────────┤
│ │
│ Backend (MongoDB) Frontend (TypeScript) │
│ ───────────────── ──────────────────── │
│ │
│ core_label { name: 'text', ... } │
│ core_backgroundColor { name: 'variant', ... } │
│ core_padding SmartButtonProperties │
│ property_templates BUTTON_EMBEDDABLE │
│ │
│ ❌ NO CONNECTION ❌ │
│ │
│ Result: │
│ • Duplicate definitions │
│ • Manual sync required │
│ • No single source of truth │
│ • AI can't leverage backend metadata │
└─────────────────────────────────────────────────────────────┘
✅ Proposed Solution: Property Reference System
Architecture: 3-Tier Property System
┌─────────────────────────────────────────────────────────────┐
│ TIER 0: Backend Property Definitions (Single Source) │
│ MongoDB: gograb-core.core_properties │
│ - core_label, core_backgroundColor, etc. │
│ - property_templates (groups) │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ TIER 1: Frontend Property Service (Runtime Resolution) │
│ @libs-foundational/core-properties │
│ - Fetches backend properties on demand │
│ - Caches for performance │
│ - Applies mappers/transforms │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ TIER 2: Embeddable/Widget Definitions (Property References) │
│ embeddables/embeddable-schemas │
│ - References backend property IDs │
│ - Minimal overrides (order, group) │
│ - MongoDB-ready (can be stored/synced) │
└─────────────────────────────────────────────────────────────┘
Data Flow
1. Embeddable Definition:
{
id: 'ui-button',
inputs: [
{ propertyId: 'core_label', override: { order: 1 } },
{ propertyId: 'core_backgroundColor', override: { order: 2 } },
{ propertyId: 'custom_buttonVariant', override: { order: 3 } }
]
}
2. Runtime Resolution (PropertyService):
- Fetch core_label from backend → { key: 'label', type: 'text', ... }
- Apply override → { ...definition, order: 1 }
- Apply mapper → { ...definition, transform: mapToString }
3. Widget Rendering:
- <ui-button [label]="resolvedProperties.core_label.value" />
🔧 Implementation Plan
Phase 1: Property Reference Schema (IMMEDIATE)
Update embeddable definitions to reference backend properties:
// OLD (Current - Hardcoded)
export const BUTTON_EMBEDDABLE = {
inputs: [
{
name: 'text',
type: 'string',
defaultValue: 'Button',
group: 'content',
},
],
};
// NEW (Property Reference)
export const BUTTON_EMBEDDABLE = {
inputs: [
{
propertyId: 'core_label', // ✅ References backend
bindTo: 'text', // Maps to component input
override: {
order: 1,
group: 'content',
},
},
{
propertyId: 'custom_buttonVariant', // Custom property in MongoDB
bindTo: 'variant',
transform: 'mapButtonVariant', // Reference to mapper
override: { order: 2 },
},
],
};
Benefits:
- ✅ Backend property updates automatically reflected
- ✅ AI context flows through from backend
- ✅ Usage tracking works (backend knows which properties are used)
- ✅ Can store entire embeddable definition in MongoDB
Phase 2: Property Service (Runtime Resolution)
Create: @libs-foundational/core-properties/src/lib/property-resolver.service.ts
@Injectable({ providedIn: 'root' })
export class PropertyResolverService {
private cache = new Map<string, PropertyDefinition>();
private http = inject(HttpClient);
/**
* Resolve a property reference to full definition
*/
async resolveProperty(
propertyId: string,
override?: Partial<PropertyOverride>
): Promise<ResolvedProperty> {
// 1. Fetch from backend (with cache)
const definition = await this.fetchProperty(propertyId);
// 2. Apply overrides
const merged = { ...definition, ...override };
// 3. Apply mapper/transform
if (merged.transform) {
merged.transformFn = this.getMapper(merged.transform);
}
return merged;
}
/**
* Resolve all properties for an embeddable
*/
async resolveEmbeddableProperties(
embeddableId: string
): Promise<Map<string, ResolvedProperty>> {
const embeddable = await this.fetchEmbeddable(embeddableId);
const resolved = new Map();
for (const input of embeddable.inputs) {
const property = await this.resolveProperty(
input.propertyId,
input.override
);
resolved.set(input.bindTo, property);
}
return resolved;
}
/**
* Get mapper function by name
*/
private getMapper(mapperName: string): (value: unknown) => any {
const mappers = {
mapButtonVariant,
mapSize,
mapColorTone,
// ... all mappers from property-mappers.ts
};
return mappers[mapperName] || ((v) => v);
}
}
Phase 3: Property Templates Support
Backend already has: property_templates (spacing, colors, typography)
Frontend should:
// Apply an entire template to a widget
const spacingProperties = await propertyResolver.resolveTemplate(
'template_spacing'
); // Returns: [padding, margin, gap] with all metadata
Phase 4: Variant System Simplification
Current (Hardcoded):
export const BUTTON_EMBEDDABLE_VARIANTS = [
{
id: 'primary-md',
name: 'Primary Medium',
inputOverrides: {
variant: 'primary',
size: 'md',
shape: 'rounded',
},
},
];
NEW (Property References):
export const BUTTON_EMBEDDABLE_VARIANTS = [
{
id: 'primary-md',
name: 'Primary Medium',
propertyValues: {
'custom_buttonVariant': 'primary',
'core_size': 'md',
'custom_buttonShape': 'rounded',
},
},
];
Stored in MongoDB as:
{
"_id": "variant-button-primary-md",
"embeddableId": "ui-button",
"name": "Primary Medium",
"propertyValues": {
"custom_buttonVariant": "primary",
"core_size": "md"
}
}
🤖 AI Agent Decision Log
Decision 1: Property Reference vs. Inline Definitions
Question: Should embeddables reference backend properties or define them inline?
Options Considered:
- Keep inline definitions (current)
- Reference backend properties by ID
- Hybrid (core properties referenced, custom inline)
Chosen: Option 2 - Full backend property references
Reasoning:
- Single Source of Truth: Backend is authoritative
- AI Training: Backend has
aiContextfield with rich metadata - Dynamic Updates: Change property in backend → instantly reflected
- MongoDB Storage: Can store entire embeddable definitions in DB
- Usage Tracking: Backend knows which properties are popular
- Reusability: Same property across multiple widgets
Confidence: 0.95
Tradeoffs:
- ✅ Pro: Eliminates duplication
- ✅ Pro: AI-friendly (all context in one place)
- ⚠️ Con: Requires network fetch (mitigated by caching)
- ⚠️ Con: Initial complexity (pays off quickly)
Decision 2: Mapper Location
Question: Where should property mappers live?
Options:
- Keep in
@libs-foundational/core-properties(current) - Move to backend (Node.js functions)
- Store mapper NAME in backend, function in frontend
Chosen: Option 3 - Hybrid approach
Reasoning:
- Mappers transform values for Angular components (browser-only)
- Backend stores mapper NAME as metadata
- Frontend resolves name → function at runtime
- Best of both worlds
Confidence: 0.90
Decision 3: Property Templates
Question: How to handle property templates (groups)?
Chosen: Full backend template support with frontend resolution
Reasoning:
- Backend already has template infrastructure (screenshot shows this)
- Templates are reusable groups (spacing, colors, typography)
- Frontend should fetch and apply entire templates
- Reduces embeddable definition size
Confidence: 0.85
📈 Expected Impact
Developer Experience
Before:
// 50+ lines of property definitions per embeddable
export const BUTTON_EMBEDDABLE = {
inputs: [
{ name: 'text', type: 'string', defaultValue: 'Button', ... },
{ name: 'variant', type: 'string', options: [...], ... },
{ name: 'size', type: 'string', options: [...], ... },
// ... 10 more properties
],
};
After:
// 5-10 lines with property references
export const BUTTON_EMBEDDABLE = {
inputs: [
{ propertyId: 'core_label', bindTo: 'text' },
{ propertyId: 'custom_buttonVariant', bindTo: 'variant' },
{ propertyId: 'core_size', bindTo: 'size' },
],
};
Reduction: ~80% less code, 100% DRY
AI Training
Before: No AI context in frontend property definitions
After: Every property has aiContext from backend:
{
"id": "core_fontSize",
"aiContext": "Use theme font sizes: xs, sm, base, lg, xl, 2xl, 3xl, 4xl. For 'giant' or 'huge' text, use 3xl or 4xl."
}
AI can now:
- Understand property semantics
- Suggest appropriate values
- Generate new widgets with proper properties
- Learn from property usage patterns
Variant Creation
Before: Hardcode variant definitions in TypeScript
After: Store in MongoDB, create via UI:
// POST /api/properties/variants
{
"embeddableId": "ui-button",
"name": "My Custom Button",
"propertyValues": {
"custom_buttonVariant": "primary",
"core_size": "lg"
}
}
Result: Non-technical users can create variants!
🎯 Next Steps
Immediate (This Session)
- ✅ Document architecture (this file)
- ⏭️ Create
PropertyReferenceDefinitioninterface - ⏭️ Update ONE embeddable (button) to use property references
- ⏭️ Create mock
PropertyResolverService(no backend calls yet)
Short Term (Next Session)
- Implement real
PropertyResolverServicewith HTTP calls - Add caching layer (IndexedDB + in-memory)
- Update all embeddables to use property references
- Create property template resolution
Medium Term
- Build variant creator UI (MongoDB CRUD)
- Integrate with widget builder
- Add property usage analytics
- AI property suggestion system
Long Term
- Custom property creation UI
- Property versioning
- Property dependency system
- Cross-widget property synchronization
🔗 Integration Points
With Module System
Modules can expose properties that are automatically registered:
export class MyModule implements BrowserModuleContract {
manifest = {
properties: [
{
id: 'my-module_customProp',
key: 'customProp',
// ... rest of PropertyDefinition
}
],
};
}
With AI Agent System
Every decision about properties should be logged:
// When selecting which properties for an embeddable
await agentService.recordDecision({
type: 'property-selection',
question: 'Which properties should ui-button have?',
options: ['core_label', 'core_icon', 'core_backgroundColor'],
chosen: ['core_label', 'custom_buttonVariant'],
reasoning: 'Buttons need text and variant for visual hierarchy',
confidence: 0.85,
});
With Widget Showcase
The widget showcase at http://localhost:5200/space/chrome-space-1/widgets/button should:
- Display all properties with their backend metadata
- Show
aiContextto help users understand properties - Allow filtering by
categoryandtags - Enable variant creation by changing property values
📚 Related Documentation
Status: 🟡 Architectural Plan (Not Implemented)
Confidence: 95%
Impact: HIGH - Unifies entire property system
Effort: Medium (2-3 sessions)