2026 03 14 Vitto Studio Live Platform Engine Design
Documentation for Plans
Vitto Studio Live Platform Engine
Session: S224 Date: 2026-03-14 Status: Design Author: Claude Code (S224.2)
Problem Statement
Vitto generates full Angular 20 platforms with feature modules (match-center, club-members, etc.), but the generated platforms cannot render in the Studio Live Preview. The root cause is a missing initialization pipeline: generated modules depend on 39 shared internal libraries (@libs-foundational/*, @libs-core/*, @libs-ui-core/*, etc.) that the rspack in-browser compiler cannot resolve. There is no yarn install, no build step, and no serve step after generation.
Secondary issues include generation template bugs (broken route imports, wrong export names) and an incomplete visual editing loop.
Competitive Context
| Platform | Architecture | Preview Latency | Limitation |
|---|---|---|---|
| Lovable ($6.6B) | Cloud Firecracker MicroVMs | 2-5s (network) | Per-session cloud cost, React/Supabase only |
| Bolt.new | WebContainers (WASM Node.js) | <1s cached | Memory ceiling kills monorepos, commercial license |
| v0.dev | Vercel Sandbox VMs | 1-3s | React/Next.js only |
| GoGrab (target) | ESM SDK + rspack WASM | <2s | Framework-native Angular, no per-session cost |
None of these competitors handle Angular monorepos with 39+ shared libraries. GoGrab's architecture is more complex, which is both the challenge and the moat.
Architecture Overview
The Key Insight: Synthesized Preview
The generated platform is for deployment (full Angular app with all files, CI/CD pipeline). The studio preview is synthesized (app shell + module routes, compiled in-browser via rspack).
The studio never needs to compile the entire generated platform. It compiles ONLY the module code and wraps it in a synthesized app shell. All 39 shared libraries are pre-built as ESM bundles on CDN, resolved via browser-native import maps.
Three-Tier Preview Architecture
Tier 1: INSTANT PREVIEW (Primary — <2s)
┌─────────────────────────────────────────────────────┐
│ VFS (module files) │
│ ↓ │
│ BundlerFilesystemService (synthesize app shell) │
│ ↓ │
│ rspack WASM (compile ONLY module code) │
│ ↓ │
│ Service Worker (serve compiled assets) │
│ ↓ │
│ iframe + import map (resolve SDK from CDN) │
│ ↓ │
│ Live Angular app in preview │
└─────────────────────────────────────────────────────┘
Tier 2: FULL BUILD (Validation — 30-60s, future)
┌─────────────────────────────────────────────────────┐
│ Cloud MicroVM (Hetzner sandbox-orchestrator) │
│ → yarn install → ng build → serve │
│ → iframe points at VM URL │
└─────────────────────────────────────────────────────┘
Tier 3: PRODUCTION DEPLOY (60-90s)
┌─────────────────────────────────────────────────────┐
│ GitLab CI → Cloudflare Pages │
│ → Full AOT build, production optimizations │
└─────────────────────────────────────────────────────┘
Why This Beats Every Competitor
- Faster than Lovable: No VM boot per session. Libraries cached on CDN, only module code compiles.
- More robust than Bolt.new: Not limited by browser memory. 39 libraries are pre-built, not compiled in-browser.
- Framework-native: Real Angular JIT compilation, not JSX-in-iframe like v0.
- Full editing loop: Click component → Vitto AI modifies → rspack recompile → instant re-render.
- Real data: Preview connects to actual backend APIs via
provideApiConfig. - Composable: Generated modules are self-contained, shareable across platforms.
Phase 1: Platform SDK Pipeline
Goal
Build all 39 shared libraries into ESM bundles, host on Cloudflare R2 with versioned paths, and generate import maps for runtime resolution.
Why ESM + Import Maps (Not WebContainers or Module Federation)
| Approach | Verdict | Reason |
|---|---|---|
| WebContainers | Eliminated | Memory ceiling for 39+ libs, commercial license, untested for Angular/Nx at scale |
| Module Federation | Eliminated | Webpack dependency, complex runtime negotiation, overkill for pre-built libs |
| Sandpack/Nodebox | Eliminated | Poor Angular support, designed for small examples |
| Cloud MicroVMs | Tier 2 only | Network latency per change, per-session cost |
| ESM + Import Maps | Selected | Browser-native, zero per-session cost, CDN-cached, <2s compile |
Library Inventory (39 libraries, 4 tiers)
Tier 0 — Pure Types (no Angular, no runtime deps):
@libs-foundational/core-models@libs-core/shared-models@libs-api/api-contracts
Tier 1 — Foundational (depends on Angular, Tier 0):
@libs-foundational/api-config@libs-foundational/core-angular(PlatformBootstrapService, guards, resolvers)@libs-foundational/core-properties@libs-foundational/connectivity-core(WebSocket, Socket.IO)@libs-foundational/storage-system(IndexedDB, flash-persistence)@libs-foundational/user-context@libs-core/log-system@libs-core/utilities@libs-core/event-system@libs-core/security@libs-dev-tools/debug-utils
Tier 2 — Core Framework (depends on Tier 1):
@libs-core/action-registry@libs-core/data-access(UniverseDataService, BaseCrudApiService)@libs-core/data-binding@libs-core/module-base(useCrudList composable)@libs-core/module-config@libs-core/module-integrator(BrowserBootstrapper)@libs-core/ui-framework(GgsCrudListShellComponent, layout primitives)@libs-core/widget-system@libs-core/mock-api
Tier 3 — UI/UX (depends on Tiers 0-2):
@libs-ui-core/ui-core@libs-ui-core/ui-feedback@libs-ui-core/ui-forms@libs-ui-core/ui-structure@libs-ux/drag-drop@libs-ux/ux-icons@libs-ux/ux-loading@libs-ux/ux-menu@libs-ux/ux-portal@libs-ux/ux-space@libs-ux/ux-utils-lib@theme-system/theme-runtime@theme-system/theme-styles@libs-ui/auth-ui@libs-private/logo-utils@libs-ai/gograb-ai-lib
Third-Party Dependencies
SDK libraries depend on non-Angular third-party packages that MUST be handled:
| Package | Used By | Handling |
|---|---|---|
@angular/cdk |
drag-drop, ui-core | External — add to import map (esm.sh) |
@angular/animations |
Several UI libs | External — add to import map (esm.sh). MUST be singleton to avoid double registration. |
socket.io-client |
connectivity-core | External — add to import map (esm.sh). CommonJS internals handled by esm.sh conversion. |
dexie |
storage-system | External — add to import map (esm.sh) |
tinycolor2 |
theme-runtime | External — add to import map (esm.sh) |
uuid |
auth-ui, others | External — add to import map (esm.sh) |
@fortawesome/* |
ux-icons | External — add to import map (esm.sh) |
yjs, y-websocket |
connectivity-core | External — add to import map (esm.sh) |
Rule: ALL runtime dependencies are externalized and resolved via import map. Zero inlining. This prevents module identity conflicts (especially with Angular's DI system) and keeps SDK bundles small.
Build Pipeline
platform-base-template/libs/
↓
tools/scripts/build-platform-sdk.ts (SWC + esbuild)
↓
dist/platform-sdk/v{version}/
├── manifest.json (library catalog + dependency graph)
├── import-map.json (browser-native import map)
├── core-models/index.js (Tier 0)
├── shared-models/index.js (Tier 0)
├── api-config/index.js (Tier 1)
├── core-angular/index.js (Tier 1)
├── module-base/index.js (Tier 2)
├── ui-core/index.js (Tier 3)
├── ui-framework/index.js (Tier 2)
└── ... (39 total)
↓
tools/scripts/deploy-platform-sdk.ts (upload to Cloudflare R2)
↓
https://sdk.gograb.space/v{version}/{lib}/index.js
Build Script Design (tools/scripts/build-platform-sdk.ts)
// Pseudocode — actual implementation will be a proper NX library
interface SdkBuildConfig {
sourceRoot: string; // platform-base-template/libs/
outputDir: string; // dist/platform-sdk/v{version}/
version: string; // semver from package.json
externalAngular: boolean; // true — Angular resolved from esm.sh
externalRxjs: boolean; // true — RxJS resolved from esm.sh
externalThirdParty: string[]; // ['socket.io-client', 'dexie', 'tinycolor2', '@angular/cdk', ...]
}
// Steps:
// 1. Read tsconfig.base.json paths → build library registry
// 2. Analyze imports → topological sort by dependency order
// 3. For each library (in dependency order):
// a. Use SWC to transpile TypeScript → JavaScript (preserving decorators for JIT)
// b. Use esbuild to bundle into single ESM file
// c. Mark @angular/*, rxjs, zone.js, third-party deps, AND other @libs-* as externals
// d. Output: {lib}/index.js + {lib}/index.js.map
// 4. Generate manifest.json (library catalog + deps + exports)
// 5. Generate import-map.json (all libs + third-party → CDN URLs)
SDK Manifest (manifest.json)
{
"version": "1.0.0",
"angularVersion": "20",
"rxjsVersion": "7",
"buildDate": "2026-03-14T00:00:00Z",
"libraries": [
{
"name": "@libs-foundational/core-angular",
"path": "core-angular/index.js",
"size": 45000,
"dependencies": ["@libs-foundational/api-config", "@libs-foundational/core-models"],
"exports": ["PlatformBootstrapService", "providePlatformBootstrap", "provideApiConfig"],
"tier": 1
}
],
"importMap": {
"imports": {
"@angular/core": "https://esm.sh/@angular/core@20",
"@libs-foundational/core-angular": "https://sdk.gograb.space/v1/core-angular/index.js",
"@libs-core/module-base": "https://sdk.gograb.space/v1/module-base/index.js"
}
}
}
Local Development Mode
For development without CDN:
- Build SDK locally →
dist/platform-sdk/v{version}/ - Serve via Angular dev server proxy:
/sdk/*→dist/platform-sdk/* - rspack resolves to
http://localhost:6200/sdk/v1/{lib}/index.js - Switch to R2 URLs via environment config for production
New NX Library: @libs-structural/platform-sdk
libs/libs-structural/platform-sdk/
├── src/
│ ├── lib/
│ │ ├── sdk-builder.service.ts # Orchestrates build
│ │ ├── sdk-manifest.ts # Manifest types + loader
│ │ ├── sdk-import-map.service.ts # Generate/inject import maps
│ │ └── sdk-resolver.service.ts # Runtime SDK URL resolution
│ └── index.ts
├── project.json
└── tsconfig.lib.json
Files to Create/Modify
New:
libs/libs-structural/platform-sdk/— New NX library (types, manifest, import map generation)tools/scripts/build-platform-sdk.ts— Build scripttools/scripts/deploy-platform-sdk.ts— R2 upload script
Modified:
libs/libs-structural/platform-preview/src/lib/config/esm-cdn-map.ts— Add all 39 SDK librarieslibs/libs-structural/platform-preview/src/lib/config/rspack-angular.config.ts— Add SDK externalslibs/libs-structural/platform-preview/src/lib/services/bundler-filesystem.service.ts— Import map injection into iframe HTMLpackage.json(root) — Addsdk:buildandsdk:deployscripts
Phase 2: Generation Pipeline Fixes
Goal
Fix the code generation templates so generated modules compile correctly against the SDK and produce valid Angular code.
Current Bugs (Verified — Confidence: Certain)
Bug 1: Route imports point to non-existent files
// WRONG (app.routes.ts):
loadChildren: () => import('../../../modules/structural/match-center/src/routes')
.then(m => m.MatchCenterRoutes)
// File 'routes.ts' does not exist. Actual: 'match-center.routes.ts'
// Export name mismatch: actual export is 'matchCenterRoutes', not 'MatchCenterRoutes'
Bug 2: Module index.ts exports from non-existent lib/ directory
// WRONG (match-center/src/index.ts):
export * from './lib/match-center.module';
// Directory 'lib/' does not exist. Files are at root of src/
Bug 3: Inconsistent route export naming
- match-center exports
matchCenterRoutes(camelCase) - club-members exports
MODULE_ROUTES(SCREAMING_SNAKE) - app.routes.ts expects
MatchCenterRoutes/ClubMembersRoutes(PascalCase)
Bug 4: Collection name typos
matchs(should bematches)coachs(should becoaches)
Fixes Required
These fixes go into the module generation prompts/templates in backend-vitto-api, NOT in the generated code itself (fixing generated code is a band-aid; fixing the generation is the cure).
- Standardize route file naming:
{module-slug}.routes.tswith export{ModuleName}Routes - Fix index.ts template: Export from
./not./lib/ - Standardize collection naming: Use correct English plurals
- Fix lazy-load import paths: Match actual file structure
Files to Modify
APIs/backend-vitto-api/src/app/module-generation/— Generation prompts and templates- Re-generate
vitto-uni-test-1modules to verify fixes
For Preview (Not Deployment)
Preview doesn't need these fixes because the preview app shell is synthesized by BundlerFilesystemService. The synthesized routes import directly from the module files in VFS, bypassing the buggy app.routes.ts entirely.
But these fixes ARE needed for deployment (GitLab CI → Cloudflare Pages) where the generated app.routes.ts must work.
Phase 3: Complete rspack Preview Integration
Goal
Wire up the full pipeline: VFS hydration → synthesized app shell → rspack compile → import maps → iframe preview. Make it work end-to-end.
Architecture
Studio loads project
↓
VfsSyncService.hydrateFromSpace() loads module files into VFS
↓
User opens Live Preview (or generation completes)
↓
PreviewService.compileRspack()
↓
BundlerFilesystemService.syncFromVfs(files):
1. Write module source files to rspack memfs
2. Synthesize app shell: main.ts, app.component.ts, app.routes.ts, app.config.ts
3. Synthesize index.html with import map (all SDK libs → CDN URLs)
4. Inject preview bridge client script
↓
BundlerService.compile():
rspack config has all SDK libs as externals
→ Only module code is compiled (TypeScript → JavaScript)
→ Output: main.js, styles.css (small, module-only bundle)
↓
PreviewServerService.updateAssets(sessionId, assets)
→ Service Worker stores compiled assets
↓
iframe loads /_preview/{sessionId}/index.html
→ Browser parses import map
→ Loads main.js (compiled module code)
→ main.js imports SDK libs → browser resolves via import map → fetches from CDN
→ Angular JIT bootstraps the app
→ Preview renders
Synthesized App Shell
BundlerFilesystemService already generates synthetic package.json and tsconfig.json. We extend it to synthesize the entire app shell from module manifests in VFS.
Synthesized main.ts:
import 'zone.js';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { appConfig } from './app.config';
bootstrapApplication(AppComponent, appConfig)
.catch(err => console.error('Preview bootstrap failed:', err));
Synthesized app.config.ts:
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { provideAnimations } from '@angular/platform-browser/animations';
import { appRoutes } from './app.routes';
import { provideApiConfig, ENVIRONMENT_PRESETS } from '@libs-foundational/api-config';
import { providePlatformBootstrap } from '@libs-foundational/core-angular';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(appRoutes, withComponentInputBinding()),
provideHttpClient(),
provideAnimations(),
provideApiConfig(ENVIRONMENT_PRESETS.localDirect),
providePlatformBootstrap({ spaceId: '{spaceId}', universeId: '{universeId}' }),
],
};
Synthesized app.routes.ts (dynamic, based on VFS modules):
import { Routes } from '@angular/router';
export const appRoutes: Routes = [
{ path: '', redirectTo: '{firstModule}', pathMatch: 'full' },
// One entry per module detected in VFS /modules/{slug}/
{
path: '{moduleSlug}',
loadChildren: () => import('./modules/{moduleSlug}/src/{moduleSlug}.routes')
.then(m => m.{ModuleName}Routes),
},
{ path: '**', redirectTo: '{firstModule}' },
];
Synthesized app.component.ts:
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterModule],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="preview-shell">
<nav class="preview-nav">
<!-- Dynamic nav links per module -->
</nav>
<main class="preview-main">
<router-outlet />
</main>
</div>
`,
styles: [`
.preview-shell { display: flex; flex-direction: column; height: 100vh; }
.preview-nav { display: flex; gap: 8px; padding: 8px 16px; background: var(--surface-1, #111); border-bottom: 1px solid var(--border, #222); }
.preview-nav a { color: var(--text-1, #e0e0e0); text-decoration: none; padding: 4px 12px; border-radius: 4px; }
.preview-nav a:hover { background: var(--surface-2, #1a1a2e); }
.preview-main { flex: 1; overflow: auto; }
`],
})
export class AppComponent {
title = 'Preview';
}
Import Map Injection
The synthesized index.html includes a <script type="importmap"> block:
Important notes:
bootstrapApplicationIS JIT-compatible in Angular 20 standalone mode. The Angular compiler (@angular/compiler) must be in the import map for JIT template compilation at runtime.- The synthesized app shell bypasses the
rewriteMainForJit()method inBundlerFilesystemService— the synthesizedmain.tsalready includeszone.jsimport directly. - Use
provideAnimations()(notprovideAnimationsAsync()) to avoid the@angular/platform-browser/animations/asyncdeep import path which requires an extra import map entry.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Platform Preview</title>
<script type="importmap">
{
"imports": {
"@angular/core": "https://esm.sh/@angular/core@20",
"@angular/common": "https://esm.sh/@angular/common@20",
"@angular/common/http": "https://esm.sh/@angular/common@20/http",
"@angular/router": "https://esm.sh/@angular/router@20",
"@angular/forms": "https://esm.sh/@angular/forms@20",
"@angular/animations": "https://esm.sh/@angular/animations@20",
"@angular/platform-browser": "https://esm.sh/@angular/platform-browser@20",
"@angular/platform-browser/animations": "https://esm.sh/@angular/platform-browser@20/animations",
"@angular/platform-browser-dynamic": "https://esm.sh/@angular/platform-browser-dynamic@20",
"@angular/compiler": "https://esm.sh/@angular/compiler@20",
"@angular/cdk": "https://esm.sh/@angular/cdk@20",
"rxjs": "https://esm.sh/rxjs@7",
"rxjs/operators": "https://esm.sh/rxjs@7/operators",
"zone.js": "https://esm.sh/[email protected]",
"socket.io-client": "https://esm.sh/socket.io-client@4",
"dexie": "https://esm.sh/dexie@4",
"tinycolor2": "https://esm.sh/tinycolor2@1",
"uuid": "https://esm.sh/uuid@13",
"@fortawesome/fontawesome-svg-core": "https://esm.sh/@fortawesome/fontawesome-svg-core@6",
"@fortawesome/angular-fontawesome": "https://esm.sh/@fortawesome/[email protected]",
"@fortawesome/free-solid-svg-icons": "https://esm.sh/@fortawesome/free-solid-svg-icons@6",
"yjs": "https://esm.sh/yjs@13",
"y-websocket": "https://esm.sh/y-websocket@3",
"@libs-foundational/api-config": "https://sdk.gograb.space/v1/api-config/index.js",
"@libs-foundational/core-angular": "https://sdk.gograb.space/v1/core-angular/index.js",
"@libs-core/module-base": "https://sdk.gograb.space/v1/module-base/index.js",
"@libs-core/data-access": "https://sdk.gograb.space/v1/data-access/index.js",
"@libs-core/ui-framework": "https://sdk.gograb.space/v1/ui-framework/index.js",
"@libs-core/security": "https://sdk.gograb.space/v1/security/index.js"
}
}
</script>
</head>
<body>
<app-root></app-root>
<script src="./main.js" type="module"></script>
</body>
</html>
Note: The full import map is generated dynamically from sdk-manifest.json. The example above shows the key entries. The SdkImportMapService generates the complete map at runtime including all 39 SDK libraries and their third-party dependencies.
SCSS Handling
Generated modules currently use SCSS. Two options:
Option A (Selected): CSS-only output for generated modules
- Modify generation prompts to output
.cssfiles instead of.scss - Theme variables are available via CSS custom properties (already how
@theme-system/theme-runtimeworks) - No SCSS compilation needed in browser
- Theme system's compiled CSS is part of the SDK
Option B (Deferred): sass.js WASM in-browser
- Add
sassWASM as rspack loader - Adds ~5MB WASM download, increases compile time
- Only needed if generated modules require Sass features (mixins, nesting, etc.)
rspack Config Updates
// rspack-angular.config.ts changes:
// 1. resolve.alias maps module paths within VFS
// 2. All SDK libs added to externals
// 3. Import map injection handled by BundlerFilesystemService
export function createRspackConfig(config: BundlerConfig) {
return {
// ... existing config ...
resolve: {
extensions: ['.ts', '.js', '.html', '.css', '.json'],
alias: config.moduleAliases ?? {}, // NEW: VFS module path aliases
},
externals: buildExternalsConfig(config.cdnMap),
};
}
VFS Path Mapping for rspack
Module files are loaded into rspack's memfs under /modules/{slug}/src/. The synthesized app.routes.ts imports modules using relative paths that resolve naturally within memfs:
// Synthesized app.routes.ts:
loadChildren: () => import('./modules/match-center/src/match-center.routes')
.then(m => m.MatchCenterRoutes)
// Resolves to /modules/match-center/src/match-center.routes.ts in memfs
No resolve.alias is needed for intra-module imports (they use relative paths like ./components/match-list/match-list.component). Inter-library imports (e.g., @libs-core/module-base) are handled by the externals config — rspack outputs them as bare import specifiers which the browser's import map resolves.
Files to Create/Modify
Modified:
libs/libs-structural/platform-preview/src/lib/services/bundler-filesystem.service.ts— Synthesize app shell + import mapslibs/libs-structural/platform-preview/src/lib/config/esm-cdn-map.ts— Full SDK + Angular CDN maplibs/libs-structural/platform-preview/src/lib/config/rspack-angular.config.ts— Module alias supportlibs/libs-structural/platform-preview/src/lib/models/bundler.types.ts— AddmoduleAliasesto BundlerConfiglibs/libs-structural/vitto-studio/src/lib/services/preview.service.ts— Pass module context to bundlerlibs/libs-structural/vitto-studio/src/lib/services/vfs-sync.service.ts— Expose module metadata for app shell synthesis
Phase 4: Visual Editing Loop
Goal
Enable click-to-select in the preview → Vitto AI modifies the component → rspack recompiles → preview updates. The "vibe coding" experience.
Current State (S205)
PreviewBridgeServicealready capturesSTUDIO_ELEMENT_SELECTEDevents with component name, selector, and propertiesStudioInterconnectionServicepropagates focus context across all tools- Vitto chat already receives page context via
VittoPageContextRegistry - What's MISSING: Vitto tools that modify component source code based on user instructions
Architecture
User clicks component in preview iframe
↓
PreviewBridgeService receives STUDIO_ELEMENT_SELECTED
{ componentName: 'MatchListComponent', selector: 'app-match-list', ... }
↓
StudioInterconnectionService updates focusContext signal
↓
VisualEditorService reads focus context + VFS file content
→ Builds rich context: component source, template, styles, model
↓
User says: "Add a status badge to each match row"
↓
Vitto receives: user message + focus context (component + source + template)
↓
Vitto uses visual editing tools:
modify_component_template({ componentName, newTemplate })
modify_component_styles({ componentName, newStyles })
add_import({ filePath, importStatement })
↓
Tools write changes to VFS via VfsService.updateFileContent()
↓
VFS change triggers PreviewService rebuild (debounced 500ms)
↓
rspack recompiles module code → Service Worker updates → iframe reloads
↓
Updated component renders in preview (<2s total)
Vitto Visual Editing Tools
New tools added to the Vitto Engine for studio context:
modify_component_template: Modify a component's HTML template
{
name: 'modify_component_template',
description: 'Modify the HTML template of an Angular component in the current module',
input: {
componentName: string, // e.g., 'MatchListComponent'
modulePath: string, // e.g., 'match-center'
newTemplate: string, // Full new template HTML
},
// Implementation: Find .component.html in VFS, update content
}
modify_component_styles: Modify a component's CSS
{
name: 'modify_component_styles',
description: 'Modify the CSS styles of an Angular component',
input: {
componentName: string,
modulePath: string,
newStyles: string,
},
}
modify_component_class: Modify a component's TypeScript class
{
name: 'modify_component_class',
description: 'Modify the TypeScript class of an Angular component (add fields, methods, etc.)',
input: {
componentName: string,
modulePath: string,
newClassContent: string,
},
}
add_component: Create a new component in a module
{
name: 'add_component',
description: 'Add a new Angular component to a module',
input: {
componentName: string,
modulePath: string,
template: string,
styles: string,
classContent: string,
},
}
modify_model: Update a data model
{
name: 'modify_model',
description: 'Modify a TypeScript interface/model in a module',
input: {
modelName: string,
modulePath: string,
newModelContent: string,
},
}
New Service: VisualEditorService
libs/libs-structural/vitto-studio/src/lib/services/visual-editor.service.ts
Responsibilities:
- Listen to
StudioInterconnectionService.focusContextchanges - When a component is selected, read its source files from VFS (
.ts,.html,.css) - Build a
VisualEditingContextwith full component source - Provide this context to Vitto's prompt via
StudioVittoBridgeService - When Vitto tools execute modifications, write changes back to VFS
- Changes trigger automatic rspack recompile → preview update
Properties Panel Integration
Beyond AI editing, users should be able to directly manipulate component properties:
- Selected component's
@Input()properties shown in the Properties panel - User changes a property value →
VisualEditorServiceupdates the component source - Same recompile flow as AI editing
This is Phase 4b (stretch goal) — requires introspection of compiled component metadata.
Files to Create/Modify
New:
libs/libs-structural/vitto-studio/src/lib/services/visual-editor.service.tsAPIs/backend-vitto-api/src/app/engine/tools/studio-visual-tools.ts— Vitto tools
Modified:
libs/libs-structural/vitto-studio/src/lib/services/studio-interconnection.service.ts— Enhanced focus context with source codelibs/libs-structural/vitto-studio/src/lib/services/studio-vitto-bridge.service.ts— Pass visual context to Vittolibs/libs-structural/vitto-studio/src/lib/components/studio-dock-shell/studio-dock-shell.component.ts— Wire visual editor
Data Flow: End-to-End Generation → Preview → Edit
1. USER: "Create a platform for a soccer club"
↓
2. VITTO: 6-phase generation pipeline
→ ENHANCE → ANALYZE → DECOMPOSE → PLAN → EXECUTE → INTEGRATE
→ Outputs: match-center module, club-members module
→ Files committed to GitLab repo
↓
3. STUDIO: Generation listener receives SSE events
→ VfsSyncService.refreshAllModules() loads new files into VFS
→ Auto-opens Live Preview panel
↓
4. PREVIEW: PreviewService.compileRspack()
→ BundlerFilesystemService synthesizes app shell
→ rspack compiles module code (SDK libs are externals)
→ Service Worker serves compiled assets
→ iframe loads with import map → Angular bootstraps
↓
5. USER: Clicks "Match List" component in preview
→ PreviewBridge sends STUDIO_ELEMENT_SELECTED
→ Properties panel shows component details
→ Vitto chat gains component context
↓
6. USER: "Add a team logo next to each match name"
→ Vitto uses modify_component_template to update MatchListComponent
→ VFS updates → rspack recompiles → preview refreshes (<2s)
↓
7. USER: Clicks "Deploy"
→ GitLab CI runs full AOT build → Cloudflare Pages
→ Live URL returned
Risk Assessment
Critical Risks
| Risk | Mitigation | Confidence |
|---|---|---|
| esm.sh CDN downtime breaks preview | Self-host Angular ESM bundles on same R2 bucket as SDK. Fallback to bundled mode. | Certain — single CDN dependency is fragile |
| Angular JIT deprecation in future versions | Monitor Angular roadmap. JIT still works in v20. Explore Angular Wiz/incremental DOM if JIT drops. | Likely — Angular team has been deprecating JIT since v13, but hasn't removed it |
| rspack WASM memory limits for large modules | Set module file count guardrails. Split very large modules. Monitor memory usage. | Likely — untested with 50+ files |
| Import map browser compatibility | Chrome/Edge: full support. Safari 16.4+: supported. Firefox: supported. No IE11 concern. | Certain — all modern browsers support import maps |
Non-Critical Risks
| Risk | Mitigation |
|---|---|
| SDK build breaks on library API change | Version SDK independently. Pin generated platforms to SDK version. |
| Preview differs from production build | Accept JIT vs AOT differences for preview. Full Build (Tier 2) covers fidelity. |
| Inter-library circular dependencies | Detected at build time by topological sort. Break cycles during SDK build. |
Success Criteria
Phase 1 (ESM SDK)
- All 39 libraries compile to ESM bundles with zero errors
-
sdk:buildscript completes in <60 seconds - Import map resolves all libraries in a browser test
- SDK hosted on R2 (or locally served for dev)
Phase 2 (Generation Fixes)
- Generated modules have correct route imports and export names
- Generated modules use CSS (not SCSS) for component styles
-
ng buildof a generated platform succeeds with zero errors
Phase 3 (rspack Preview)
- Studio Live Preview renders generated platform modules
- First compile <10s, incremental compile <2s
- File edit in code editor → preview updates within 2s
- CRUD components render with proper layout and navigation
Phase 4 (Visual Editing)
- Click component in preview → component highlighted + selected
- Vitto can modify component template/styles/class via natural language
- Modification → recompile → preview update in <3s total
- At least 3 Vitto visual editing tools functional
Implementation Order & Dependencies
Phase 1 (ESM SDK) ←── BLOCKER for Phase 3
↓
Phase 2 (Gen Fixes) ←── Independent, can parallel with Phase 1
↓
Phase 3 (rspack Integration) ←── Requires Phase 1 complete
↓
Phase 4 (Visual Editing) ←── Requires Phase 3 complete
Phases 1 and 2 can run in parallel. Phase 3 depends on Phase 1. Phase 4 depends on Phase 3.
Out of Scope (Deferred)
- Tier 2 Full Build Preview (Cloud MicroVM) — Future session
- SCSS in-browser compilation (sass.js WASM) — Only if CSS proves insufficient
- Hot Module Replacement — Full rebuild is acceptable at <2s
- Multi-workspace module sharing — Separate architectural decision
- Properties panel direct manipulation (Phase 4b) — Stretch goal
- Offline-first VFS sync — IndexedDB caching deferred
- SDK auto-update mechanism — Manual version bumps for now