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:

Goal: Create a unified, intelligent property system where:

  1. Backend properties are the single source of truth
  2. Frontend smart properties consume backend definitions
  3. Widgets/embeddables reference backend property IDs (MongoDB-ready)
  4. AI can generate new properties and understand existing ones
  5. 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:

Property Templates (from screenshot):

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:

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:


🚨 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:

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:

  1. Keep inline definitions (current)
  2. Reference backend properties by ID
  3. Hybrid (core properties referenced, custom inline)

Chosen: Option 2 - Full backend property references

Reasoning:

Confidence: 0.95

Tradeoffs:

Decision 2: Mapper Location

Question: Where should property mappers live?

Options:

  1. Keep in @libs-foundational/core-properties (current)
  2. Move to backend (Node.js functions)
  3. Store mapper NAME in backend, function in frontend

Chosen: Option 3 - Hybrid approach

Reasoning:

Confidence: 0.90

Decision 3: Property Templates

Question: How to handle property templates (groups)?

Chosen: Full backend template support with frontend resolution

Reasoning:

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:

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)

  1. ✅ Document architecture (this file)
  2. ⏭️ Create PropertyReferenceDefinition interface
  3. ⏭️ Update ONE embeddable (button) to use property references
  4. ⏭️ Create mock PropertyResolverService (no backend calls yet)

Short Term (Next Session)

  1. Implement real PropertyResolverService with HTTP calls
  2. Add caching layer (IndexedDB + in-memory)
  3. Update all embeddables to use property references
  4. Create property template resolution

Medium Term

  1. Build variant creator UI (MongoDB CRUD)
  2. Integrate with widget builder
  3. Add property usage analytics
  4. AI property suggestion system

Long Term

  1. Custom property creation UI
  2. Property versioning
  3. Property dependency system
  4. 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:


📚 Related Documentation


Status: 🟡 Architectural Plan (Not Implemented)
Confidence: 95%
Impact: HIGH - Unifies entire property system
Effort: Medium (2-3 sessions)