LANDING EDITOR COMPREHENSIVE PLAN

Documentation for Plans

Landing Editor Comprehensive Implementation Plan

๐Ÿ“‹ Plan Overview

Goal: Fix and enhance the landing-editor component with Flash-persistence, localStorage-based user identification, AI assistance, and skip logic.

Alignment:


๐ŸŽฏ Phase 1: Analysis & Architecture (CURRENT)

1.1 Architecture Alignment Analysis

gograb-space Patterns Identified:

gograb-ai Capabilities Available:

editor Monorepo Current State:

1.2 Key Architectural Decisions

Decision Rationale Implementation
User ID Storage localStorage (like gograb-space JWT) Synchronous access, auth migration ready
State Management Flash-persistence (autoFlashState) Automatic persistence, signal-based
AI Integration gograb-ai self-contained Enhanced onboarding, smart suggestions
Event Communication EventBus pattern Loosely coupled, scalable
Testing Strategy Vitest (not build) Faster feedback, serve-based analysis

๐Ÿ”ง Phase 2: User Identification System

2.1 Create UserIdManager Service

Location: libs/storage-system/src/core/user-context/user-id-manager.ts

Requirements:

Implementation:

import { Injectable, signal, computed } from '@angular/core';

export interface UserIdentity {
  userId: string;
  createdAt: string;
  type: 'simulated' | 'firebase' | 'jwt';
}

@Injectable({ providedIn: 'root' })
export class UserIdManager {
  private readonly USER_KEY = 'gg_editor_user_id';
  private readonly _identity = signal<UserIdentity | null>(null);
  
  readonly currentUserId = computed(() => this._identity()?.userId ?? '');
  readonly userIdentity = computed(() => this._identity());
  
  constructor() {
    this.initializeUser();
  }
  
  private initializeUser(): void {
    const stored = localStorage.getItem(this.USER_KEY);
    
    if (stored) {
      try {
        this._identity.set(JSON.parse(stored));
        return;
      } catch {
        // Invalid stored data, create new
      }
    }
    
    // Create new simulated user
    const identity: UserIdentity = {
      userId: `user-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`,
      createdAt: new Date().toISOString(),
      type: 'simulated'
    };
    
    localStorage.setItem(this.USER_KEY, JSON.stringify(identity));
    this._identity.set(identity);
  }
  
  migrateToFirebase(firebaseUid: string): void {
    // Future: Migration helper
    const identity: UserIdentity = {
      userId: firebaseUid,
      createdAt: new Date().toISOString(),
      type: 'firebase'
    };
    localStorage.setItem(this.USER_KEY, JSON.stringify(identity));
    this._identity.set(identity);
  }
}

Export: Update libs/storage-system/src/core/user-context/index.ts

2.2 Update provideGgStorage (Optional User Scoping)

Location: libs/storage-system/src/angular/providers/provide-gg-storage.ts

Add Configuration:

export interface GgStorageConfig {
  namespace?: string;
  cache?: CacheConfig;
  strategy?: StorageStrategy;
  userScoped?: boolean; // NEW: Enable user-scoped keys
}

// In provider function:
if (config.userScoped) {
  const userIdManager = inject(UserIdManager);
  // Intercept keys and prefix with user/{userId}/
  // Example: flash:preferences โ†’ flash:user/user-123/preferences
}

๐ŸŽจ Phase 3: Flash-Persistence Integration

3.1 Convert Landing Component

Location: libs/editor-web-lib/src/lib/components/landing-editor/landing-editor.component.ts

Current Issues:

New Implementation:

import { Component, signal, computed, inject, OnInit, OnDestroy } from '@angular/core';
import { Router } from '@angular/router';
import { autoFlashState } from '@storage-system';
import { ProjectDetectionService } from '../../services/project-detection.service';

interface LandingPreferences {
  showLandingByDefault: boolean;
  rememberLastProject: boolean;
  visits: number;
  lastVisit: string;
  lastProjectId?: string;
}

interface RecentProject {
  id: string;
  name: string;
  description: string;
  lastModified: Date;
  thumbnail?: string;
}

@Component({
  selector: 'editor-landing',
  standalone: true,
  // ... imports
  changeDetection: ChangeDetectionStrategy.OnPush,
  templateUrl: './landing-editor.component.html',
  styleUrls: ['./landing-editor.component.scss']
})
export class LandingComponent implements OnInit, OnDestroy {
  private router = inject(Router);
  private projectDetection = inject(ProjectDetectionService);
  
  // ๐Ÿ”ฅ Flash-persistence for preferences
  private landingPrefs = autoFlashState<LandingPreferences>(
    'editor-preferences/landing',
    {
      showLandingByDefault: true,
      rememberLastProject: false,
      visits: 0,
      lastVisit: new Date().toISOString()
    }
  );
  
  // ๐Ÿ”ฅ Flash-persistence for recent projects
  private recentProjectsState = autoFlashState<RecentProject[]>(
    'editor-preferences/recent-projects',
    []
  );
  
  // Computed signals for UI
  showLandingByDefault = computed(() => this.landingPrefs().showLandingByDefault);
  rememberLastProject = computed(() => this.landingPrefs().rememberLastProject);
  recentProjects = computed(() => this.recentProjectsState());
  
  async ngOnInit() {
    // Track visit
    this.landingPrefs.update(prefs => ({
      ...prefs,
      visits: prefs.visits + 1,
      lastVisit: new Date().toISOString()
    }));
    
    // Check auto-skip
    await this.checkShouldSkip();
    
    // Load recent projects from storage
    this.loadRecentProjects();
    
    // Initialize animations
    this.initializeParticleSystem();
    this.initializeNeuralNetwork();
  }
  
  private async checkShouldSkip() {
    const prefs = this.landingPrefs();
    const hasProjects = await this.projectDetection.hasExistingProjects();
    
    if (!prefs.showLandingByDefault && hasProjects) {
      this.startEditing();
    }
  }
  
  toggleLandingPreference() {
    this.landingPrefs.update(prefs => ({
      ...prefs,
      showLandingByDefault: !prefs.showLandingByDefault
    }));
  }
  
  toggleRememberProject() {
    this.landingPrefs.update(prefs => ({
      ...prefs,
      rememberLastProject: !prefs.rememberLastProject
    }));
  }
  
  onSkipLanding() {
    this.landingPrefs.update(prefs => ({
      ...prefs,
      showLandingByDefault: false
    }));
    this.startEditing();
  }
  
  // ... rest of component
}

Key Structure:

flash:editor-preferences/landing -> LandingPreferences
flash:editor-preferences/recent-projects -> RecentProject[]
flash:editor-web/pages -> Page data (for detection)

3.2 Verify Component Dependencies

ExecutionLogsComponent: โœ… Uses HttpClient only - no changes needed LiveActivityComponent: โœ… Uses UnifiedEventsService only - no changes needed


๐Ÿ” Phase 4: Project Detection Service

4.1 Create ProjectDetectionService

Location: libs/editor-web-lib/src/lib/services/project-detection.service.ts

import { Injectable, inject } from '@angular/core';
import { GgStorage } from '@storage-system';

@Injectable({ providedIn: 'root' })
export class ProjectDetectionService {
  private storage = inject(GgStorage);
  
  async hasExistingProjects(): Promise<boolean> {
    try {
      // Check multiple possible keys
      const keys = [
        'flash:editor-web/pages',
        'flash:editor-preferences/recent-projects'
      ];
      
      for (const key of keys) {
        const value = await this.storage.get(key);
        
        if (Array.isArray(value) && value.length > 0) {
          return true;
        }
        
        if (value && typeof value === 'object') {
          const entries = Object.keys(value);
          if (entries.length > 0) {
            return true;
          }
        }
      }
      
      return false;
    } catch (error) {
      console.warn('ProjectDetection: Error checking projects', error);
      return false;
    }
  }
  
  async getLastProject(): Promise<string | null> {
    try {
      const prefs = await this.storage.get('flash:editor-preferences/landing');
      return prefs?.lastProjectId || null;
    } catch {
      return null;
    }
  }
  
  async getProjectCount(): Promise<number> {
    try {
      const value = await this.storage.get('flash:editor-web/pages');
      
      if (Array.isArray(value)) {
        return value.length;
      }
      
      if (value && typeof value === 'object') {
        return Object.keys(value).length;
      }
      
      return 0;
    } catch {
      return 0;
    }
  }
}

Export: libs/editor-web-lib/src/lib/services/index.ts


๐Ÿ›ก๏ธ Phase 5: Landing Guard with Skip Logic

5.1 Create Landing Guard

Location: libs/editor-web-lib/src/lib/guards/landing.guard.ts

import { inject } from '@angular/core';
import { Router, CanActivateFn } from '@angular/router';
import { ProjectDetectionService } from '../services/project-detection.service';
import { GgStorage } from '@storage-system';

export const landingGuard: CanActivateFn = async () => {
  const router = inject(Router);
  const projectDetection = inject(ProjectDetectionService);
  const storage = inject(GgStorage);
  
  try {
    // Load preferences
    const prefs = await storage.get('flash:editor-preferences/landing');
    
    // Check for existing projects
    const hasProjects = await projectDetection.hasExistingProjects();
    
    // Skip logic
    if (prefs && !prefs.showLandingByDefault && hasProjects) {
      router.navigate(['/editor/workspace']);
      return false;
    }
    
    if (prefs && prefs.rememberLastProject && hasProjects) {
      const lastProject = await projectDetection.getLastProject();
      if (lastProject) {
        router.navigate(['/editor/workspace'], {
          queryParams: { pageId: lastProject }
        });
        return false;
      }
    }
    
    // Show landing
    return true;
  } catch (error) {
    console.warn('LandingGuard: Error in skip logic', error);
    return true; // Show landing on error
  }
};

Export: libs/editor-web-lib/src/lib/guards/index.ts


๐Ÿค– Phase 6: AI Integration (NEW)

6.1 Add AI Assistant to Landing

Purpose: Help new users get started with AI suggestions

Implementation in Landing Component:

// Add to LandingComponent
import { AiChatComponent } from '@gograb-ai';

// In component class
showAiHelper = signal(false);
aiChatId = 'landing-assistant';

// In template
@if (showAiHelper()) {
  <div class="ai-helper-overlay">
    <lib-ai-chat
      [chatId]="aiChatId"
      (configurationChanged)="onAiConfigChanged($event)"
      (conversationStarted)="onConversationStarted()"
    />
  </div>
}

// Button to toggle
<button 
  class="ai-help-button" 
  (click)="showAiHelper.set(!showAiHelper())">
  <i class="fas fa-robot"></i>
  AI Help
</button>

AI Helper Features:

6.2 AI-Powered Quick Actions

Implementation:

quickActions = computed<QuickAction[]>(() => [
  {
    id: 'new-widget',
    title: 'Create Widget',
    description: 'Start with a blank canvas and build your widget from scratch',
    icon: '๐ŸŽจ',
    action: () => this.createNewWidget(),
    color: 'primary'
  },
  {
    id: 'ai-generate',
    title: 'AI Generate',
    description: 'Describe what you want and let AI create it for you',
    icon: '๐Ÿค–',
    action: () => this.openAiGenerator(),
    color: 'secondary'
  },
  // ... more actions
]);

private async openAiGenerator() {
  this.showAiHelper.set(true);
  // Pre-fill with generation prompt
}

๐Ÿ”„ Phase 7: Update Routes

7.1 Update Editor Routes

Location: libs/editor-web-lib/src/lib/editor.routes.ts

import { Routes } from '@angular/router';
import { landingGuard } from './guards/landing.guard';

export const EditorRoutes: Routes = [
  {
    path: '',
    redirectTo: 'landing',
    pathMatch: 'full'
  },
  {
    path: 'landing',
    loadComponent: () =>
      import('./components/landing-editor/landing-editor.component')
        .then(m => m.LandingComponent),
    canActivate: [landingGuard],
    title: 'Welcome to Editor'
  },
  {
    path: 'workspace',
    loadComponent: () =>
      import('./components/editor-workspace/editor-workspace.component')
        .then(m => m.EditorWorkspaceComponent),
    title: 'Editor Workspace'
  },
  {
    path: 'execution-logs',
    loadComponent: () =>
      import('./components/execution-logs/execution-logs.component')
        .then(m => m.ExecutionLogsComponent),
    title: 'Execution Logs'
  },
  {
    path: 'widgets',
    loadComponent: () =>
      import('./components/widgets/widgets-panel.component')
        .then(m => m.WidgetsPanelComponent),
    title: 'Widgets'
  }
];

๐Ÿงช Phase 8: Unit Testing

8.1 UserIdManager Tests

Location: libs/storage-system/src/core/user-context/user-id-manager.spec.ts

import { TestBed } from '@angular/core/testing';
import { UserIdManager } from './user-id-manager';

describe('UserIdManager', () => {
  let service: UserIdManager;
  
  beforeEach(() => {
    localStorage.clear();
    TestBed.configureTestingModule({});
    service = TestBed.inject(UserIdManager);
  });
  
  it('should generate user ID on first load', () => {
    expect(service.currentUserId()).toMatch(/^user-\d+-[a-z0-9]+$/);
  });
  
  it('should persist user ID across instances', () => {
    const userId1 = service.currentUserId();
    const service2 = TestBed.inject(UserIdManager);
    expect(service2.currentUserId()).toBe(userId1);
  });
  
  it('should have signal reactivity', () => {
    const userId = service.currentUserId();
    expect(userId).toBeTruthy();
  });
  
  it('should handle Firebase migration', () => {
    service.migrateToFirebase('firebase-uid-123');
    expect(service.currentUserId()).toBe('firebase-uid-123');
    expect(service.userIdentity()?.type).toBe('firebase');
  });
});

8.2 ProjectDetectionService Tests

Location: libs/editor-web-lib/src/lib/services/project-detection.service.spec.ts

import { TestBed } from '@angular/core/testing';
import { ProjectDetectionService } from './project-detection.service';
import { GgStorage } from '@storage-system';

describe('ProjectDetectionService', () => {
  let service: ProjectDetectionService;
  let mockStorage: jasmine.SpyObj<GgStorage>;
  
  beforeEach(() => {
    mockStorage = jasmine.createSpyObj('GgStorage', ['get']);
    
    TestBed.configureTestingModule({
      providers: [
        { provide: GgStorage, useValue: mockStorage }
      ]
    });
    
    service = TestBed.inject(ProjectDetectionService);
  });
  
  it('should detect no projects when storage is empty', async () => {
    mockStorage.get.and.returnValue(Promise.resolve([]));
    const result = await service.hasExistingProjects();
    expect(result).toBe(false);
  });
  
  it('should detect projects when array has items', async () => {
    mockStorage.get.and.returnValue(Promise.resolve([{ id: '1' }]));
    const result = await service.hasExistingProjects();
    expect(result).toBe(true);
  });
  
  it('should detect projects when object has keys', async () => {
    mockStorage.get.and.returnValue(Promise.resolve({ page1: {} }));
    const result = await service.hasExistingProjects();
    expect(result).toBe(true);
  });
  
  it('should handle storage errors gracefully', async () => {
    mockStorage.get.and.returnValue(Promise.reject('Error'));
    const result = await service.hasExistingProjects();
    expect(result).toBe(false);
  });
  
  it('should get project count', async () => {
    mockStorage.get.and.returnValue(Promise.resolve([1, 2, 3]));
    const count = await service.getProjectCount();
    expect(count).toBe(3);
  });
});

8.3 Landing Guard Tests

Location: libs/editor-web-lib/src/lib/guards/landing.guard.spec.ts

import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { landingGuard } from './landing.guard';
import { ProjectDetectionService } from '../services/project-detection.service';
import { GgStorage } from '@storage-system';

describe('landingGuard', () => {
  let mockRouter: jasmine.SpyObj<Router>;
  let mockProjectDetection: jasmine.SpyObj<ProjectDetectionService>;
  let mockStorage: jasmine.SpyObj<GgStorage>;
  
  beforeEach(() => {
    mockRouter = jasmine.createSpyObj('Router', ['navigate']);
    mockProjectDetection = jasmine.createSpyObj('ProjectDetectionService', [
      'hasExistingProjects',
      'getLastProject'
    ]);
    mockStorage = jasmine.createSpyObj('GgStorage', ['get']);
    
    TestBed.configureTestingModule({
      providers: [
        { provide: Router, useValue: mockRouter },
        { provide: ProjectDetectionService, useValue: mockProjectDetection },
        { provide: GgStorage, useValue: mockStorage }
      ]
    });
  });
  
  it('should show landing for first-time users', async () => {
    mockStorage.get.and.returnValue(Promise.resolve(null));
    mockProjectDetection.hasExistingProjects.and.returnValue(Promise.resolve(false));
    
    const result = await TestBed.runInInjectionContext(() => landingGuard());
    expect(result).toBe(true);
  });
  
  it('should skip landing when disabled and has projects', async () => {
    mockStorage.get.and.returnValue(Promise.resolve({
      showLandingByDefault: false
    }));
    mockProjectDetection.hasExistingProjects.and.returnValue(Promise.resolve(true));
    
    const result = await TestBed.runInInjectionContext(() => landingGuard());
    expect(result).toBe(false);
    expect(mockRouter.navigate).toHaveBeenCalledWith(['/editor/workspace']);
  });
  
  it('should navigate to last project when remember is enabled', async () => {
    mockStorage.get.and.returnValue(Promise.resolve({
      rememberLastProject: true
    }));
    mockProjectDetection.hasExistingProjects.and.returnValue(Promise.resolve(true));
    mockProjectDetection.getLastProject.and.returnValue(Promise.resolve('page-123'));
    
    const result = await TestBed.runInInjectionContext(() => landingGuard());
    expect(result).toBe(false);
    expect(mockRouter.navigate).toHaveBeenCalledWith(
      ['/editor/workspace'],
      { queryParams: { pageId: 'page-123' } }
    );
  });
});

8.4 Landing Component Integration Tests

Location: libs/editor-web-lib/src/lib/components/landing-editor/landing-editor.component.spec.ts

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LandingComponent } from './landing-editor.component';
import { ProjectDetectionService } from '../../services/project-detection.service';
import { Router } from '@angular/router';

describe('LandingComponent', () => {
  let component: LandingComponent;
  let fixture: ComponentFixture<LandingComponent>;
  let mockProjectDetection: jasmine.SpyObj<ProjectDetectionService>;
  let mockRouter: jasmine.SpyObj<Router>;
  
  beforeEach(async () => {
    mockProjectDetection = jasmine.createSpyObj('ProjectDetectionService', [
      'hasExistingProjects'
    ]);
    mockRouter = jasmine.createSpyObj('Router', ['navigate']);
    
    await TestBed.configureTestingModule({
      imports: [LandingComponent],
      providers: [
        { provide: ProjectDetectionService, useValue: mockProjectDetection },
        { provide: Router, useValue: mockRouter }
      ]
    }).compileComponents();
    
    fixture = TestBed.createComponent(LandingComponent);
    component = fixture.componentInstance;
  });
  
  it('should create', () => {
    expect(component).toBeTruthy();
  });
  
  it('should toggle landing preference', () => {
    const initial = component.showLandingByDefault();
    component.toggleLandingPreference();
    expect(component.showLandingByDefault()).toBe(!initial);
  });
  
  it('should toggle remember project preference', () => {
    const initial = component.rememberLastProject();
    component.toggleRememberProject();
    expect(component.rememberLastProject()).toBe(!initial);
  });
  
  it('should skip landing on button click', () => {
    component.onSkipLanding();
    expect(component.showLandingByDefault()).toBe(false);
    expect(mockRouter.navigate).toHaveBeenCalled();
  });
});

๐Ÿ” Phase 9: Linter Analysis (Using Serve)

9.1 Start Development Server

# Start editor-web serve (port 5200)
nx serve editor-web

# OR start specific app if testing there
nx serve ux-test

9.2 Check Console for Errors

Monitor browser console for:

9.3 Manual Testing Checklist


๐Ÿ“š Phase 10: Documentation

10.1 Create Documentation Structure

Following @add-documentation.md guidelines:

Structure:

docs/english/GoGrab-Space/editor-web/
โ”œโ”€โ”€ index.md (landing page overview)
โ”œโ”€โ”€ landing-page.md (this implementation)
โ”œโ”€โ”€ flash-persistence-integration.md
โ””โ”€โ”€ ai-integration.md

docs/spanish/GoGrab-Space/editor-web/
โ”œโ”€โ”€ index.es.md
โ””โ”€โ”€ landing-page.es.md (placeholder)

10.2 Landing Page Documentation

Location: docs/english/GoGrab-Space/editor-web/landing-page.md

---
title: Landing Page Implementation
description: Complete guide to the editor landing page with Flash-persistence and AI integration
icon: ๐Ÿš€
order: 1
crossLanguageId: editor-landing-page
languageVersions:
  en: GoGrab-Space/editor-web/landing-page
  es: GoGrab-Space/editor-web/landing-page
---

# Landing Page Implementation

## Overview

The editor landing page provides a welcoming introduction to new users with automatic skip logic for returning users.

## Features

### Flash-Persistence Integration

The landing page uses `autoFlashState` for automatic state persistence:

\```typescript
private landingPrefs = autoFlashState<LandingPreferences>(
  'editor-preferences/landing',
  {
    showLandingByDefault: true,
    rememberLastProject: false,
    visits: 0,
    lastVisit: new Date().toISOString()
  }
);
\```

### AI Assistant

New users can get help from an AI assistant:

\```typescript
<lib-ai-chat
  [chatId]="'landing-assistant'"
  (conversationStarted)="onConversationStarted()"
/>
\```

### Skip Logic

The landing guard automatically skips the landing page for returning users:

- Users with `showLandingByDefault: false` skip to workspace
- Users with `rememberLastProject: true` go to last project
- New users always see the landing page

## Configuration

### User Preferences

\```typescript
interface LandingPreferences {
  showLandingByDefault: boolean;
  rememberLastProject: boolean;
  visits: number;
  lastVisit: string;
  lastProjectId?: string;
}
\```

## Usage

### Accessing the Landing Page

Navigate to `/editor/landing` to see the landing page.

### Skipping the Landing

Click the skip button or disable in preferences:

\```typescript
toggleLandingPreference() {
  this.landingPrefs.update(prefs => ({
    ...prefs,
    showLandingByDefault: !prefs.showLandingByDefault
  }));
}
\```

## Architecture

### Storage Keys

- `flash:editor-preferences/landing` - User preferences
- `flash:editor-preferences/recent-projects` - Recent projects list
- `gg_editor_user_id` - User identification (localStorage)

### Services

- `ProjectDetectionService` - Detects existing projects
- `UserIdManager` - Manages user identification
- `LandingGuard` - Route guard with skip logic

## Best Practices

1. Always use Flash-persistence for user preferences
2. Test skip logic with multiple scenarios
3. Ensure AI assistant is non-intrusive
4. Monitor performance with particle animations

## See Also

- [Flash-Persistence Integration](/GoGrab-Space/editor-web/flash-persistence-integration)
- [AI Integration](/GoGrab-Space/editor-web/ai-integration)
- [Storage System](/GoGrab-Space/storage-system/overview)

10.3 Flash-Persistence Integration Docs

Location: docs/english/GoGrab-Space/editor-web/flash-persistence-integration.md

---
title: Flash-Persistence Integration
description: How the editor uses Flash-persistence for automatic state management
icon: โšก
order: 2
---

# Flash-Persistence Integration

## Overview

The editor integrates Flash-persistence for automatic state persistence without manual localStorage management.

## Key Concepts

### autoFlashState

Replace manual localStorage with automatic persistence:

\```typescript
// โŒ Manual localStorage (old way)
localStorage.setItem('prefs', JSON.stringify(prefs));

// โœ… Flash-persistence (new way)
const prefs = autoFlashState('editor-preferences/landing', defaultPrefs);
prefs.update(p => ({ ...p, visits: p.visits + 1 }));
\```

### Key Structure

All editor keys follow a consistent pattern:

- `flash:editor-preferences/*` - User preferences
- `flash:editor-web/pages/*` - Page data
- `flash:editor-ui/*` - UI state

## Examples

### User Preferences

\```typescript
const landingPrefs = autoFlashState('editor-preferences/landing', {
  showLandingByDefault: true,
  rememberLastProject: false
});

// Reactive UI
showLandingByDefault = computed(() => landingPrefs().showLandingByDefault);
\```

### Recent Projects

\```typescript
const recentProjects = autoFlashState<RecentProject[]>(
  'editor-preferences/recent-projects',
  []
);

// Add project
recentProjects.update(projects => [...projects, newProject]);
\```

## Migration from localStorage

### Before

\```typescript
class PreferencesService {
  private preferences = signal({ theme: 'dark' });
  
  async loadPreferences() {
    const stored = localStorage.getItem('preferences');
    if (stored) this.preferences.set(JSON.parse(stored));
  }
  
  async savePreferences(prefs: Preferences) {
    this.preferences.set(prefs);
    localStorage.setItem('preferences', JSON.stringify(prefs));
  }
}
\```

### After

\```typescript
class PreferencesService {
  preferences = autoFlashState('preferences', { theme: 'dark' });
  
  // That's it! No load/save needed
}
\```

## Performance

Flash-persistence provides:
- โšก 0.3ms RAM cache reads
- ๐Ÿ’พ Automatic persistence (debounced)
- ๐Ÿง  Intelligent storage adapter selection
- ๐Ÿ”„ Optional MongoDB sync

## See Also

- [Storage System Overview](/GoGrab-Space/storage-system/overview)
- [Flash-Persistence API](/GoGrab-Space/storage-system/flash-persistence/api-reference)

10.4 Create Index Files

Location: docs/english/GoGrab-Space/editor-web/index.md

---
title: Editor Web Library
description: Documentation for the editor-web-lib Angular library
icon: โœ๏ธ
order: 1
---

# Editor Web Library

The `editor-web-lib` library provides the core editor functionality for the GoGrab Space platform.

## Features

- ๐Ÿš€ **Landing Page** - Welcoming introduction with AI assistance
- โœ๏ธ **Editor Workspace** - Visual page editor
- ๐ŸŽจ **Widget System** - Drag-and-drop widget builder
- โšก **Flash-Persistence** - Automatic state management
- ๐Ÿค– **AI Integration** - Smart suggestions and generation

## Quick Start

\```typescript
// Import editor routes
import { EditorRoutes } from '@editor-web-lib';

// Add to app routes
export const routes: Routes = [
  {
    path: 'editor',
    children: EditorRoutes
  }
];
\```

## Documentation

- [Landing Page](/GoGrab-Space/editor-web/landing-page)
- [Flash-Persistence Integration](/GoGrab-Space/editor-web/flash-persistence-integration)
- [AI Integration](/GoGrab-Space/editor-web/ai-integration)

## Architecture

The editor follows a modular architecture with clear separation:

- **Components** - UI components with OnPush strategy
- **Services** - Business logic and state management
- **Guards** - Route protection and skip logic
- **State** - Flash-persistence for automatic persistence

โœ… Success Criteria

Functionality

Testing

Documentation

Performance


๐Ÿ”„ Execution Order

  1. โœ… Analysis Complete (Phase 1)
  2. โณ Create UserIdManager (Phase 2.1) - NEXT
  3. โณ Update provideGgStorage (Phase 2.2)
  4. โณ Convert Landing Component (Phase 3)
  5. โณ Create ProjectDetectionService (Phase 4)
  6. โณ Create Landing Guard (Phase 5)
  7. โณ Add AI Integration (Phase 6)
  8. โณ Update Routes (Phase 7)
  9. โณ Create Unit Tests (Phase 8)
  10. โณ Linter Analysis (Phase 9)
  11. โณ Create Documentation (Phase 10)

๐Ÿ“‹ Implementation Tracking

Track progress with todo items linked to this plan.

Start Date: Today Target Completion: Based on execution pace Approach: Iterative with continuous testing via serve