WIDGET DEVELOPMENT STANDARDS

Ecosistema completo para organización y gestión de contenido con interfaces espaciales

Widget Development Standards - AI-First Edition

A comprehensive guide for developing widgets in the GoGrab Space page builder. Every widget must be AI-ready, responsive, and provide an excellent editing experience.

Table of Contents

  1. The Widget Development Checklist
  2. Required Files Structure
  3. AI Context & Training Schema
  4. Property Templates (JSON)
  5. Preview Components
  6. Inline Editing Requirements
  7. Widget Categories & Capabilities
  8. AI-First Development Pattern
  9. Validation & Error Handling
  10. Testing Guidelines

The Widget Development Checklist

Every widget MUST have:

1. TypeScript Interface

Location: libs/libs-widgets/widgets/src/lib/{widget-name}/{widget-name}.types.ts

/**
 * List Widget Configuration
 */
export interface ListWidgetConfig {
  items: string[];
  listType: 'ordered' | 'unordered' | 'none';
  markerStyle?: string;
  itemSpacing?: number;
}

2. JSON Property Template

Location: libs/libs-foundational/core-properties/src/lib/templates/{widget-name}.template.json

Every property MUST include AI metadata for training purposes.

3. Preview Component with Inline Editing

Location: libs/libs-widgets/widgets/src/lib/{widget-name}/components/{widget-name}-preview.component.ts

Must support:

4. Widget Registration

Location: libs/libs-widgets/widget-system/src/lib/registry/unified-widget-registry.service.ts

this.registerWidget({
  id: 'list',
  type: 'list',
  displayName: 'List',
  icon: 'fa-list',
  category: 'content',
  capabilities: {
    canResize: true,
    canMove: true,
    canDelete: true,
    canDuplicate: true,
    hasInlineEdit: true,
    aiGenerable: true,
  },
});

5. Validation Rules

Every widget must validate its configuration:

export function validateListConfig(config: ListWidgetConfig): ValidationResult {
  const errors: string[] = [];
  
  if (!config.items || !Array.isArray(config.items)) {
    errors.push('Items must be an array');
  }
  
  return {
    valid: errors.length === 0,
    errors,
  };
}

Required Files Structure

libs/libs-widgets/widgets/src/lib/{widget-name}/
├── {widget-name}.types.ts           # TypeScript interfaces
├── {widget-name}.widget.ts          # Widget registration
├── components/
│   ├── {widget-name}-preview.component.ts   # Preview/edit component
│   ├── {widget-name}-preview.component.html
│   └── {widget-name}-preview.component.scss
├── validators/
│   └── {widget-name}.validator.ts   # Config validation
└── index.ts                         # Barrel exports

libs/libs-foundational/core-properties/src/lib/templates/
└── {widget-name}.template.json      # Property definitions with AI context

AI Context & Training Schema

Every widget must export an AI training schema for Space Agents.

Schema Structure

{
  "widgetType": "list",
  "version": "1.0.0",
  "aiCapabilities": {
    "canGenerate": true,
    "canModify": true,
    "canStyle": true,
    "supportsInlinePrompts": true
  },
  "naturalLanguageExamples": [
    "Add a bulleted list with 3 items",
    "Change the list to numbered",
    "Add 'Introduction' to the list",
    "Make the list items have more spacing"
  ],
  "propertyMappings": {
    "items": {
      "naturalLanguage": ["items", "list items", "bullets", "entries", "points"],
      "type": "array<string>",
      "aiGenerable": true,
      "importance": "essential"
    },
    "listType": {
      "naturalLanguage": ["numbered", "bulleted", "ordered", "unordered", "type"],
      "type": "enum",
      "values": ["unordered", "ordered", "none"],
      "importance": "high"
    },
    "itemSpacing": {
      "naturalLanguage": ["spacing", "gap", "space between"],
      "type": "number",
      "unit": "px",
      "constraints": { "min": 0, "max": 48 },
      "importance": "medium"
    }
  },
  "contextualPrompts": {
    "empty": "This list is empty. What items should I add?",
    "fewItems": "Add more items to this list?",
    "manyItems": "This list has {count} items. Should I organize them?"
  }
}

AI Property Metadata

Each property in the JSON template MUST include:

{
  "id": "list-items",
  "key": "items",
  "label": "List Items",
  "controlType": "array",
  "ai": {
    "importance": "essential",
    "prompt": "Modify list items",
    "examples": [
      "Add 'Buy groceries' to the list",
      "Change first item to 'Introduction'",
      "Add 5 benefits of our product"
    ],
    "constraints": ["Must be non-empty strings"],
    "useCases": ["list content", "bullet points", "steps", "features"]
  }
}

Importance Levels

Level Description Example Properties
essential Core content, always needed Text content, list items, image src
high Important for appearance/function Variant, size, listType
medium Useful customization Spacing, colors, alignment
low Advanced/optional Animation, custom CSS

Property Templates (JSON)

Complete Template Example

{
  "id": "template_list",
  "name": "List",
  "category": "content",
  "builtin": true,
  "order": 10,
  "widgetTypes": ["list"],
  "properties": [
    {
      "id": "list-items",
      "key": "items",
      "label": "List Items",
      "controlType": "array",
      "category": "content",
      "order": 1,
      "defaultValue": ["Item 1", "Item 2", "Item 3"],
      "ai": {
        "importance": "essential",
        "prompt": "Modify list items",
        "examples": ["Add 'Buy groceries'", "Remove second item"],
        "useCases": ["list content", "bullet points"]
      }
    },
    {
      "id": "list-type",
      "key": "listType",
      "label": "List Type",
      "controlType": "select",
      "category": "appearance",
      "order": 2,
      "defaultValue": "unordered",
      "validation": {
        "options": [
          { "label": "Bulleted", "value": "unordered" },
          { "label": "Numbered", "value": "ordered" }
        ]
      },
      "ai": {
        "importance": "high",
        "prompt": "Change list style",
        "examples": ["Make it numbered", "Use bullets"]
      }
    }
  ]
}

Control Types

Type Description Use For
text Single-line text input Short text, URLs
textarea Multi-line text Long content, descriptions
number Numeric input Sizes, counts
slider Range slider Spacing, opacity
boolean Toggle switch Enable/disable features
select Dropdown menu Fixed options
color Color picker Colors
array List editor Items, tags

Preview Components

Requirements

  1. Separate Files: .ts, .html, .scss (NO inline templates)
  2. OnPush Change Detection: For performance
  3. Signals: Use Angular signals for state
  4. Input/Output: Standard component communication

Template

@Component({
  selector: 'lib-list-preview',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './list-preview.component.html',
  styleUrl: './list-preview.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ListPreviewComponent {
  /** Widget configuration */
  config = input.required<ListWidgetConfig>();
  
  /** Emitted when config changes */
  configChange = output<Partial<ListWidgetConfig>>();
  
  /** Currently editing item index */
  editingIndex = signal<number | null>(null);
  
  /** Computed list items */
  items = computed(() => this.config().items || []);
  
  /** Computed styles */
  styles = computed(() => ({
    listStyleType: this.getMarkerStyle(),
    gap: `${this.config().itemSpacing || 8}px`,
  }));
}

Inline Editing Requirements

Every Editable Widget Must Support:

  1. Click to Edit: Click on content to enter edit mode
  2. Blur to Save: Clicking outside saves changes
  3. Enter to Confirm: Pressing Enter saves and exits edit mode
  4. Escape to Cancel: Pressing Escape cancels changes
  5. Delete Handling: Backspace on empty removes item (for arrays)
  6. Tab Navigation: Tab moves to next editable item
  7. AI Integration: Button/shortcut to invoke AI assistance

Example Implementation

<!-- List Item Inline Editing -->
<li class="list-item" [class.editing]="editingIndex() === idx">
  <input
    #itemInput
    type="text"
    [value]="item"
    (focus)="startEditing(idx)"
    (blur)="finishEditing(idx, $event)"
    (keydown.enter)="addItemAfter(idx, $event)"
    (keydown.escape)="cancelEditing()"
    (keydown.backspace)="maybeRemoveItem(idx, $event)"
    [attr.aria-label]="'List item ' + (idx + 1)"
  />
</li>

<!-- AI Add Items Button -->
<button class="ai-add-btn" (click)="showAIItemPrompt()">
  <i class="fas fa-magic"></i>
  Ask AI to add items
</button>

Widget Categories & Capabilities

Categories

Category Widgets Description
Basic Text, Heading, Button, List, Link Fundamental content widgets
Layout Section, Container, Grid Structural widgets
Media Image, Video, Icon Visual content
Form Input, Textarea, Select, Checkbox User input
Data Table, Chart, Card Data display
Navigation Nav, Breadcrumb, Tabs Navigation elements

Capability Matrix

Widget AI Gen AI Modify Inline Edit Responsive Has Children
Text ✅ ✅ ✅ (contenteditable) ✅ ❌
Heading ✅ ✅ ✅ (contenteditable) ✅ ❌
Button ✅ ✅ ✅ (text only) ✅ ❌
List ✅ ✅ ✅ (each item) ✅ ❌
Link ✅ ✅ ✅ (text + URL modal) ✅ ❌
Section ✅ ✅ ✅ (name + layout) ✅ ✅
Container ✅ ✅ ✅ (layout controls) ✅ ✅
Grid ✅ ✅ ✅ (column adjust) ✅ ✅
Image ✅ AI gen ✅ ⚠️ (upload/URL/AI) ✅ ❌
Video ✅ AI gen ✅ ⚠️ (upload/URL/AI) ✅ ❌
Icon ✅ ✅ ✅ (icon picker) ✅ ❌
Divider ❌ ✅ ❌ ✅ ❌
Spacer ❌ ✅ ⚠️ (drag handle) ✅ ❌

AI-First Development Pattern

1. Define Widget Schema with AI Metadata

export interface WidgetConfig {
  property: string;
  ai: PropertyAIMetadata;  // ← Required
}

interface PropertyAIMetadata {
  importance: 'essential' | 'high' | 'medium' | 'low';
  prompt: string;
  examples: string[];
  constraints?: string[];
}

2. Create Property Template JSON with AI Prompts

{
  "properties": [{
    "key": "property",
    "ai": {
      "prompt": "Change widget property",
      "examples": ["Make it blue", "Set to 100px"]
    }
  }]
}

3. Implement Preview with Inline AI

<div class="widget-ai-controls">
  <input placeholder="Tell AI what to change..." />
  <button (click)="executeAIPrompt()">
    <i class="fas fa-magic"></i>
  </button>
</div>

4. Export AI Training Data

// Every widget should export getAIContext()
widget.getAIContext() → Used for Space Agent training

Validation & Error Handling

Config Validation

interface ValidationResult {
  valid: boolean;
  errors?: string[];
  warnings?: string[];
}

export function validateConfig(config: WidgetConfig): ValidationResult {
  const errors: string[] = [];
  const warnings: string[] = [];

  // Required field validation
  if (!config.items?.length) {
    errors.push('List must have at least one item');
  }

  // Type validation
  if (typeof config.itemSpacing !== 'number') {
    warnings.push('Item spacing should be a number');
  }

  // Range validation
  if (config.itemSpacing && (config.itemSpacing < 0 || config.itemSpacing > 100)) {
    errors.push('Item spacing must be between 0 and 100');
  }

  return { valid: errors.length === 0, errors, warnings };
}

Error Boundary

Widgets should gracefully handle errors:

@Component({
  template: `
    @if (hasError()) {
      <div class="widget-error">
        <i class="fas fa-exclamation-triangle"></i>
        <span>Widget failed to render</span>
        <button (click)="retry()">Retry</button>
      </div>
    } @else {
      <ng-content></ng-content>
    }
  `
})
export class WidgetErrorBoundary { }

Testing Guidelines

Unit Tests Required

  1. Config Validation: Test all validation rules
  2. Default Values: Verify defaults are applied correctly
  3. AI Context: Ensure AI metadata is complete
  4. Computed Properties: Test derived values

Integration Tests

  1. Inline Editing: Test edit mode enter/exit
  2. Keyboard Navigation: Test all shortcuts
  3. AI Integration: Test prompt handling
  4. Responsive Behavior: Test breakpoint changes

Example Test

describe('ListPreviewComponent', () => {
  it('should render all items', () => {
    const config = { items: ['A', 'B', 'C'], listType: 'ordered' };
    component.config.set(config);
    expect(component.items()).toEqual(['A', 'B', 'C']);
  });

  it('should enter edit mode on item click', () => {
    component.startEditing(0);
    expect(component.editingIndex()).toBe(0);
  });

  it('should emit config change on edit', () => {
    const spy = vi.spyOn(component.configChange, 'emit');
    component.updateItem(0, 'New Value');
    expect(spy).toHaveBeenCalledWith({ items: ['New Value', 'B', 'C'] });
  });
});

Quick Reference

Adding a New Widget

  1. Create type interface in {widget-name}.types.ts
  2. Create JSON property template in templates/{widget-name}.template.json
  3. Create preview component with inline editing
  4. Register in widget registry
  5. Add AI training schema
  6. Write tests

AI Property Checklist

Files to Update When Adding Widget

  1. core-properties/src/lib/templates/{widget}.template.json
  2. core-properties/src/lib/templates/index.ts
  3. widget-system/src/lib/registry/unified-widget-registry.service.ts
  4. widgets/src/lib/{widget}/index.ts
  5. widgets/src/index.ts

Last Updated: Session 55 - AI-First Page Builder Enhancement