2026 03 23 Proactive Visual Intelligence Design
Documentation for Plans
Proactive Visual Intelligence (PVI) — Design Specification
Date: 2026-03-23 Status: Approved Sub-project: 1 of 3 (PVI → Generation Pipeline Quality → Platform Editability) Session: 257
1. Vision
GoGrab Space is an AI-first platform builder. When a user generates a platform, Vitto doesn't just build it and walk away — Vitto proactively analyzes every component, identifies enhancement opportunities, and presents them as one-click improvements. Users click anywhere in the live preview to see what Vitto suggests. Over time, Vitto learns each user's preferences and gets smarter.
This is not a web builder with AI features bolted on. This is an AI co-pilot that understands design systems, code architecture, and user intent — and acts on all three simultaneously.
2. Core User Flow
- Generate — User describes a platform, Vitto generates it
- Analyze — Vitto scans every generated component against the SDK manifest, then runs parallel LLM analysis. Ambient pulsing dots appear on elements with suggestions. A chat summary shows the top 3 ranked improvements with "Apply All."
- Click — User clicks any element in the live preview. A floating toolbar appears with category icons (Style, Layout, Component, UX, Data, Accessibility). Toolbar ordering is personalized based on what the user applies most.
- Suggest — User clicks a category icon. Instant rule-based suggestions appear. Background LLM enrichment streams in within 1-2 seconds with smarter, context-aware suggestions.
- Apply — Style changes inject CSS instantly into the iframe, then persist to SCSS. Structural changes generate code via LLM, write to VFS, and compile with a shimmer indicator. Auto-rollback on compilation failure.
- Undo — Every apply creates a VFS snapshot (local-first, async Hetzner git push). Timeline shows change history. Click any point to restore.
- Learn — Every interaction is tracked. Vitto adjusts toolbar ordering, suggestion ranking, and eventually the generation pipeline itself based on what users actually apply.
3. Architecture
3.1 System Layers
┌─────────────────────────────────────────────────────────────┐
│ LAYER 0: PREREQUISITES │
│ ComponentTaggingPlugin (data-studio-id injection) │
│ SDK Manifest Pipeline (.intelligence.ts → sdk-manifest.json)│
├─────────────────────────────────────────────────────────────┤
│ LAYER 1: ELEMENT INTELLIGENCE (preview iframe) │
│ Enhanced preview-bridge-client.ts │
│ Rich ElementSelectionPayload via postMessage │
│ Ambient dot rendering + CSS injection handlers │
├─────────────────────────────────────────────────────────────┤
│ LAYER 2: SUGGESTION ENGINE (studio services) │
│ SuggestionRuleEngine (instant, <10ms, from manifest) │
│ SuggestionEnrichmentService (LLM, Haiku/Sonnet routing) │
│ SuggestionMergeEngine (rule + LLM → ranked results) │
├─────────────────────────────────────────────────────────────┤
│ LAYER 3: PROACTIVE ANALYSIS (studio + vitto-api) │
│ Post-gen rule scan (<100ms) → ambient dots + prelim summary│
│ Parallel Haiku deep scan (2-4s) → merged results │
│ Chat summary with ranked top-3 + "Apply All" │
├─────────────────────────────────────────────────────────────┤
│ LAYER 4: APPLY ENGINE (studio + preview) │
│ StyleInjectionService (CSS inject → SCSS persist) │
│ StructuralApplyService (LLM code gen → VFS → compile) │
│ Auto-rollback on compilation failure │
│ Apply All with conflict detection │
├─────────────────────────────────────────────────────────────┤
│ LAYER 5: SNAPSHOT & UNDO (studio + Hetzner) │
│ Local-first VFS snapshots (instant undo) │
│ Async Hetzner git push (durable history) │
│ Timeline UI with restore-to-point │
├─────────────────────────────────────────────────────────────┤
│ LAYER 6: LEARNING (vitto-api + MongoDB) │
│ UserSuggestionTracker (view/apply/dismiss events) │
│ SuggestionWeightService (personalized ordering) │
│ Feedback quality loop (success/undo tracking) │
│ Generation pipeline feedback (V3) │
└─────────────────────────────────────────────────────────────┘
3.2 Builds on Existing Infrastructure
| Existing System | How PVI Extends It |
|---|---|
preview-bridge-client.ts (click-to-select) |
Enhanced payload, CSS injection, ambient dots |
VisualEditorService (reads VFS sources) |
Feeds component sources to suggestion engine |
StudioInterconnectionService (focus tracking) |
PVI subscribes to focus updates |
BundlerFilesystemService (VFS enrichment) |
SCSS patching, VFS snapshots |
PreviewServerService (blob iframe) |
CSS injection via postMessage |
BundlerService (incremental compile) |
Structural apply triggers compile (auto-selects PATCH path for ≤6 changed files) |
StudioVittoBridgeService (Vitto comms) |
Chat summary, LLM enrichment calls |
UniverseGitService (Hetzner git) |
Snapshot commits |
BaseLLMProvider + ProviderFactory |
PVI task routes for model selection |
ComponentTaggingPlugin (data-studio-id) |
Must be completed — V1 prerequisite |
| Prompt partials (ui-framework-components) | Auto-generated from sdk-manifest.json |
| Vitto Memory (MongoDB vector search) | Learning layer storage |
3.3 New Libraries and Services
New NX library: @libs-ai/proactive-visual-intelligence
- Angular DI-free core (no
@Injectable, noinject()— pure functions and classes). Deeply tied to Angular component concepts (selectors, inputs, outputs) but has zero Angular runtime dependency. - Exports:
SuggestionRuleEngine,SuggestionMergeEngine,ComponentIntelligenceService,SuggestionWeightService,SuggestionCategorytype, all PVI interfaces - Consumed by: vitto-studio (Angular), vitto-api (NestJS), any future consumer
New services in existing libraries:
| Service | Library | Purpose |
|---|---|---|
StyleInjectionService |
@libs-structural/platform-preview |
CSS injection into blob iframe |
ProactiveAnalysisService |
@libs-structural/vitto-studio |
Post-generation scan orchestration |
SnapshotService |
@libs-structural/vitto-studio |
Local VFS snapshots + Hetzner push |
UserSuggestionTracker |
backend-vitto-api |
MongoDB event recording |
PviModelRouterService |
backend-vitto-api |
Smart model selection for PVI tasks |
New components in vitto-studio:
| Component | Purpose |
|---|---|
PviToolbarComponent |
Floating category icon toolbar |
PviSuggestionPanelComponent |
Expandable suggestion list per category |
PviTimelineComponent |
Horizontal snapshot history strip |
PviChatSummaryComponent |
Ranked top-3 card in Vitto chat |
4. Unified Component Intelligence Manifest
4.1 Problem
The generation pipeline, preview stubs, and prompt partials are maintained separately. This causes drift:
- Prompt partials describe only 10 of 46+ SDK components (46
ggs-*components +tabswhich does not follow theggs-*naming convention) - Preview stubs have different contracts than real components
- No machine-readable description of what each component CAN do
4.2 Solution: SDK Manifest Pipeline
Each SDK component gets a .intelligence.ts file alongside its source:
libs/core/ui-framework/src/lib/components/ggs-table/
├── ggs-table.component.ts
├── ggs-table.component.html
├── ggs-table.component.scss
└── ggs-table.intelligence.ts ← NEW
An NX target (generate-sdk-manifest) aggregates all intelligence files into sdk-manifest.json:
*.intelligence.ts (47 files) → generate-sdk-manifest → sdk-manifest.json
4.3 ComponentIntelligence Interface
interface ComponentIntelligence {
selector: string;
category: 'button' | 'form' | 'data' | 'layout' | 'overlay' | 'state' | 'navigation' | 'upload' | 'chart';
purpose: string;
whenToUse: string[];
whenNotToUse: string[];
inputs: Record<string, {
type: string;
required: boolean;
default?: unknown;
description: string;
options?: string[];
}>;
outputs: Record<string, {
payload: string;
description: string;
}>;
variants: Record<string, string[]>;
suggestions: Record<SuggestionCategory, string[]>;
upgradesFrom: string[];
composedWith: string[];
themeTokens: string[];
examples: Array<{
label: string;
html: string;
ts?: string;
}>;
}
type SuggestionCategory = 'style' | 'layout' | 'component' | 'ux' | 'data' | 'accessibility';
4.4 SDK Manifest JSON Structure
{
"version": "1.0.0",
"generatedAt": "2026-03-23T...",
"components": {
"ggs-table": { "...ComponentIntelligence..." }
},
"composables": {
"useCrudList": {
"purpose": "...",
"provides": ["items", "loading", "..."],
"methods": ["loadData", "onSearch", "..."],
"pairsWith": ["ggs-crud-list-shell", "ggs-table"]
}
},
"patterns": {
"crud-list": {
"description": "Standard entity list page",
"requires": ["ggs-crud-list-shell", "ggs-table", "useCrudList"],
"optional": ["ggs-filter-panel", "ggs-search-toolbar"],
"template": "..."
}
},
"themeSystem": {
"tokens": {
"--color-primary": { "fallback": "#2563eb", "category": "color", "usage": "..." }
}
}
}
4.5 Consumers
| Consumer | What It Reads | Benefit |
|---|---|---|
| Generation prompts | components, composables, patterns |
LLM knows exactly what to use (all 47, not 10) |
| Proactive analysis | suggestions, upgradesFrom |
Rule-based instant suggestions |
| LLM enrichment | Full manifest subset per component | Context-aware AI suggestions |
| Preview stubs | inputs, outputs |
Auto-alignment with real components |
| Learning layer | Writes back suggestion weights | Manifest evolves from usage data |
| External agents | Full JSON | Any agent can build/edit platforms |
4.6 Drift Elimination
The prompt partial ui-framework-components.partial.md is auto-generated from the manifest. The NX target outputs both sdk-manifest.json and the markdown partial. Hand-maintained partials are deleted. Change a component → update .intelligence.ts → run build → prompts, stubs, and suggestions all update.
5. Layer 1: Element Intelligence (Preview Iframe)
5.1 Enhanced Element Selection Payload
The preview bridge client (preview-bridge-client.ts) runs inside the blob iframe. On click, it gathers rich context about the clicked element:
interface ElementSelectionPayload {
studioId: string; // data-studio-id attribute value
componentName: string; // Parsed from studioId
tagName: string; // DOM tag name
childGgsComponents: string[]; // ggs-* elements inside this component
missingGgsComponents: string[]; // Raw HTML that could be SDK components
hasNgFor: boolean; // Template uses *ngFor pattern
hasFormControls: boolean; // Contains input/select/textarea elements
computedStyles: Record<string, string>; // Key CSS properties
boundingRect: { x: number; y: number; width: number; height: number }; // Serialized (DOMRect not cloneable via postMessage)
}
Compatibility note: This is a new message type PVI_ELEMENT_CONTEXT sent alongside the existing STUDIO_ELEMENT_SELECTED. The existing StudioElementPayload type and PreviewSelectionEvent in StudioInterconnectionService remain unchanged — PVI subscribes to the new message type via PreviewBridgeService. This avoids breaking the current click-to-select flow. Types to create: ElementSelectionPayload (in PVI library), handler in PreviewBridgeService.handleMessage() for PVI_ELEMENT_CONTEXT.
How it gathers this: On click, walk the DOM subtree scoped to the same data-studio-id. Identify child elements with ggs- tag prefix. Identify raw HTML elements that match upgrade patterns (<button>, <table>, <span> with status-like text). Read computed styles for layout/color/shadow detection. All synchronous DOM inspection, <5ms.
5.2 New Inbound Message Handlers
| Message Type | Payload | Behavior |
|---|---|---|
PVI_INJECT_CSS |
{ studioId, properties: Record<string, string> } |
Apply CSS properties to target element via style.setProperty() |
PVI_REMOVE_CSS |
{ studioId, properties: string[] } |
Remove CSS properties, restore original values from stored snapshot |
PVI_INJECT_DOTS |
Array<{ studioId, dotType, suggestions }> |
Render pulsing indicator dots on target elements |
PVI_CLEAR_DOTS |
void |
Remove all dots |
5.3 Ambient Dot Rendering
Dots are absolutely-positioned <div> elements anchored to elements with matching data-studio-id. On iframe load event (after recompile), all dots are re-queried and repositioned. If the target element no longer exists, the dot is silently removed.
Granularity limitation: data-studio-id is injected on component root elements only (by studioTaggingLoader). Dots anchor to components, not individual child elements. If a user clicks a child <button> inside a ggs-table, the dot appears on the table's root element. Per-element granularity is a V3 consideration.
Two visual tiers:
- High-impact (red, larger pulse): ≥1 high-impact suggestion for this component
- Enhancement (purple, subtle pulse): medium/low impact suggestions only
5.4 CSS Injection for Style Apply
The bridge client stores original inline style values before injection. On PVI_REMOVE_CSS, originals are restored. On iframe reload (recompile), injected styles are naturally cleared (the compiled SCSS now includes the change).
6. Layer 2: Suggestion Engine
6.1 SuggestionRuleEngine
Pure synchronous function. No LLM, no network. Input: ElementSelectionPayload + sdk-manifest.json + UserCategoryWeights. Output: Suggestion[].
~20 rules for V1, expandable:
| Rule | Match Condition | Suggestion |
|---|---|---|
| List without table | hasNgFor && !childGgsComponents.includes('ggs-table') |
Layout: Switch to ggs-table |
| List without cards | hasNgFor && !computedStyles.display.includes('grid') |
Layout: Switch to card grid |
| Raw button | missingGgsComponents.includes('<button>') |
Component: Replace with ggs-button |
| Status text without badge | missingGgsComponents.includes('<span>') + status-like text |
Component: Use ggs-badge |
| No empty state | List component without ggs-empty-state child | UX: Add empty state |
| No shadows on cards | `!computedStyles.boxShadow | |
| Hardcoded colors | Computed style contains hex values not in theme tokens | Style: Use theme tokens |
| No search in list | List without ggs-search-input child | UX: Add search |
| No filters in list | List without ggs-filter-panel child | Data: Add filter panel |
| Form without error display | Form without error message elements | UX: Add validation errors |
| Missing loading state | Component without ggs-loading-spinner | UX: Add loading indicator |
| Table without pagination | ggs-table without ggs-pagination | Data: Add pagination |
| No hover effects | computedStyles show no transition properties | Style: Add hover transitions |
| No responsive breakpoints | Fixed widths detected | Layout: Add responsive grid |
| Missing accessibility labels | Form inputs without associated labels | A11y: Add label associations |
Rules return suggestions with source: 'rule'. Suggestions for style changes include pre-built StylePatch with both cssPreview and scssPatch. Structural suggestions include StructuralChange with a pre-built LLM prompt.
6.2 SuggestionEnrichmentService
Async LLM enrichment. Fires conditionally after 500ms debounce measured from when PVI_ELEMENT_CONTEXT arrives at the suggestion engine (note: StudioInterconnectionService applies an additional 150ms debounce on focus updates, so total click-to-enrichment is ~650ms).
Conditions to fire:
- User focused on element for ≥500ms (after interconnection debounce)
- Rule engine produced <3 suggestions for the active category
- No cached result for this
componentName:category - No in-flight enrichment call (cancel previous on new click)
Model routing:
- Simple components (button, badge, input): Haiku (~1s)
- Complex components (table, form, dashboard, shell): Sonnet (~2-3s)
Prompt structure:
Analyze this Angular component for {category} improvements.
Component: {componentName}
Template: {html source}
Styles: {scss source}
Available SDK: {relevant manifest entries}
User's platform context: {business domain, entity names}
Return 2-3 specific, actionable suggestions.
JSON: { suggestions: [{ label, description, impact, applyType, codeHint }] }
codeHint is a brief description of the code change — NOT the actual code. Full code generation happens only at apply time.
Cache: Map<string, Suggestion[]> keyed by componentName:category. Invalidated when VFS files for that component change.
6.3 SuggestionMergeEngine
Combines rule-based and LLM suggestions:
- LLM suggestions that match a rule suggestion (same category + similar label) → LLM version wins (richer description)
- LLM suggestions with no rule match → added as new
- Rule suggestions with no LLM match → kept as-is
- Sort by: impact (high > medium > low), then user's category weights
7. Layer 3: Proactive Analysis
7.1 Post-Generation Scan
Triggered when generation completes and preview compiles successfully.
Phase A: Rule Scan (instant, <100ms, studio-side)
- Iterate all
.component.tsand.component.htmlfiles in VFS - For each component, run lightweight static analysis:
- Parse template for raw HTML elements upgradeable to SDK components
- Check SCSS for hardcoded hex colors
- Check for missing composable usage (useCrudList, useCrudForm)
- Check for missing ggs-empty-state in list templates
- Cross-reference with sdk-manifest.json suggestions
- Produce preliminary
PviAnalysisResult - Inject ambient dots immediately
- Post preliminary chat message: "Analyzing your platform..."
Phase B: LLM Deep Scan (2-4s, parallel Haiku)
- For each component: fire Haiku call with template + SCSS + manifest context
- All calls in parallel via
forkJoin(RxJS — no Promise.all per project conventions) - Each call asks: "What specific improvements would make this component more professional, usable, and polished? Consider all 6 categories."
- Merge LLM results with rule results via
SuggestionMergeEngine - Update ambient dots (some upgrade from enhancement to high-impact)
- Post final chat summary with ranked top-3
7.2 Chat Summary
One message per analysis pass. Structure:
✨ I found {N} enhancements for your platform:
[🎨] Card layout for Animals list — high impact [Apply]
[🧩] Status badges for Active/Draft — high impact [Apply]
[✨] Empty state for zero results — medium impact [Apply]
[Apply All Top 3]
+ {N-3} more — click the ✨ dots in the preview to explore.
7.3 Smart Model Router
interface PviModelRoute {
task: string;
model: 'haiku' | 'sonnet' | 'opus';
maxTokens: number;
temperature: number;
}
const PVI_ROUTES: Record<string, PviModelRoute> = {
'pvi:component-scan': { model: 'haiku', maxTokens: 2048, temperature: 0.2 },
'pvi:enrich-simple': { model: 'haiku', maxTokens: 2048, temperature: 0.3 },
'pvi:enrich-complex': { model: 'sonnet', maxTokens: 4096, temperature: 0.3 },
'pvi:structural-apply': { model: 'sonnet', maxTokens: 8192, temperature: 0.1 },
'pvi:cross-module-analysis': { model: 'opus', maxTokens: 16384, temperature: 0.2 },
};
Plugs into existing BaseLLMProvider + ProviderFactory via task key lookup.
8. Layer 4: Apply Engine
8.1 Style Apply (Instant)
User clicks "Apply" on style suggestion
│
├── 1. SnapshotService.createLocalSnapshot(affectedPaths)
├── 2. PreviewBridgeService.sendPviMessage('PVI_INJECT_CSS', patch.cssPreview)
│ → Bridge client applies inline styles instantly (new public method on PreviewBridgeService)
├── 3. StyleInjectionService.patchScssFile(patch.scssPath, patch.scssPatch, patch.insertAfterSelector)
│ → Reads SCSS from VFS, patches string, writes back via VfsService.updateFileContent()
│ → This is NEW infrastructure — no patchScssFile exists today
├── 4. Background: incremental compile (preview doesn't flash — CSS already showing)
└── 5. Background: SnapshotService.pushToHetzner(commitMessage)
SCSS patching: Not AST manipulation. Generated SCSS files follow predictable BEM structure. Find the target selector block as a string, insert the new property line. If selector doesn't exist, append. This is reliable for generated code which follows strict conventions.
StylePatch interface:
interface StylePatch {
cssPreview: Record<string, string>; // Injected into iframe DOM
scssPatch: string; // Written to .scss file in VFS
scssPath: string; // Target .scss file path
insertAfterSelector: string; // Where in SCSS to insert
}
8.2 Structural Apply (Compiled)
User clicks "Apply" on structural suggestion
│
├── 1. SnapshotService.createLocalSnapshot(affectedPaths)
├── 2. Show shimmer overlay on target element
├── 3. LLM call (Sonnet) with:
│ - Current component files (TS, HTML, SCSS)
│ - sdk-manifest.json entries for involved components
│ - suggestion.structuralChange.prompt
│ - "Return ONLY modified files as JSON. Must compile."
├── 4. Write modified files to VFS
├── 5. Write to VFS triggers normal compile pipeline (BundlerService auto-selects incremental PATCH path for ≤6 files)
│
├── On SUCCESS:
│ - Remove shimmer
│ - Preview refreshes with compiled result
│ - Background: SnapshotService.pushToHetzner()
│ - UserSuggestionTracker.record({ applied: true })
│
└── On FAILURE (compilation error):
- Auto-rollback: restore VFS from local snapshot
- Remove shimmer
- Show error badge on suggestion: "Couldn't apply"
- NO Hetzner commit (failed state never persisted)
- UserSuggestionTracker.record({ applied: false, error: true })
8.3 Apply All with Conflict Detection
Each suggestion declares conflicts: string[] — IDs of incompatible suggestions.
"Apply All" clicked
│
├── 1. Sort top suggestions by impact (high first)
├── 2. Create single local snapshot (batch undo point)
├── 3. For each suggestion in order:
│ ├── Check conflicts against already-applied set
│ ├── If conflict → skip, add to skipped[]
│ ├── If style → batch into single CSS injection
│ └── If structural → apply sequentially (each needs compile)
│ Show progress: "Applying 2 of 3..." between structural applies
├── 4. Show result: "Applied {N} of {M}. Skipped: {reasons}"
└── 5. Single Hetzner commit for the batch
9. Layer 5: Snapshot & Undo
9.1 Local-First Architecture
interface VfsSnapshot {
id: string;
timestamp: number;
label: string; // e.g., "Added card shadow"
affectedPaths: string[]; // Which files were changed
fileContents: Record<string, string>; // File content before the change (Record, not Map — must be JSON-serializable)
hetznerCommitSha?: string; // Populated async after push
hetznerPushStatus: 'pending' | 'pushed' | 'failed';
}
Local stack: Max 50 snapshots. FIFO eviction. Undo restores from memory in <1ms.
Hetzner push: Async after successful apply. Commit message: [PVI] {suggestion.label}. 3x retry with exponential backoff. Failure is non-blocking — user can still undo locally.
9.2 Undo Flow
User clicks Undo (or Ctrl+Z)
│
├── 1. Get previous snapshot from local stack
├── 2. Restore VFS files from snapshot.fileContents (<1ms)
├── 3. If style change was undone: postMessage('PVI_REMOVE_CSS', ...)
├── 4. Incremental compile → preview refreshes (~2-4s)
└── 5. Timeline UI updates current position
9.3 Timeline UI
Horizontal strip below the preview. Each dot = a snapshot. Current position highlighted. Labels on hover. Click any dot to jump to that state (non-linear undo). Shows Hetzner sync status per dot (checkmark = pushed, spinner = pending).
10. Layer 6: Learning
10.1 Event Tracking
Every PVI interaction recorded:
interface UserSuggestionEvent {
userId: string;
spaceId: string;
universeId: string;
componentType: string;
category: SuggestionCategory;
suggestionLabel: string;
action: 'viewed' | 'applied' | 'dismissed' | 'undone';
source: 'rule' | 'llm' | 'merged';
timestamp: Date;
}
Stored in MongoDB pvi_user_preferences collection on vitto-mind database.
10.2 User Preference Weights
Daily cron aggregation:
interface UserCategoryWeights {
userId: string;
weights: Record<SuggestionCategory, number>; // 0.0-1.0, normalized
topPatterns: Array<{
componentType: string;
preferredSuggestions: string[];
}>;
updatedAt: Date;
}
Cold start defaults: style=0.30, layout=0.25, component=0.20, ux=0.15, data=0.05, accessibility=0.05.
Weights shift after ~10 interactions. Strongly personalized after ~50.
10.3 How Weights Flow Back
| Consumer | Effect |
|---|---|
| Toolbar icon ordering | Highest-weight category appears first |
| Rule engine | More suggestion slots for preferred categories |
| Chat summary | User's preferred categories weighted in top-3 ranking |
| LLM enrichment | Skip enrichment for categories user rarely engages with |
11. Key Interfaces
// Core suggestion type
interface Suggestion {
id: string;
category: SuggestionCategory;
label: string;
description: string;
impact: 'high' | 'medium' | 'low';
applyType: 'style' | 'structural';
source: 'rule' | 'llm' | 'merged' | 'learned';
conflicts: string[];
patch?: StylePatch;
structuralChange?: StructuralChange;
}
// Style change — dual format
interface StylePatch {
cssPreview: Record<string, string>;
scssPatch: string;
scssPath: string;
insertAfterSelector: string;
}
// Structural change — LLM-generated at apply time
interface StructuralChange {
prompt: string; // Pre-built LLM prompt for this change
affectedFiles: string[]; // Which VFS files will change
}
// Enhanced element payload from preview bridge
interface ElementSelectionPayload {
studioId: string;
componentName: string;
tagName: string;
childGgsComponents: string[];
missingGgsComponents: string[];
hasNgFor: boolean;
hasFormControls: boolean;
computedStyles: Record<string, string>;
boundingRect: { x: number; y: number; width: number; height: number };
}
// Full analysis result
interface PviAnalysisResult {
components: Array<{
componentName: string;
studioId: string;
suggestions: Suggestion[];
dotType: 'high-impact' | 'enhancement';
}>;
summary: {
total: number;
byCategory: Record<SuggestionCategory, number>;
topThree: Suggestion[];
};
}
// VFS snapshot
interface VfsSnapshot {
id: string;
timestamp: number;
label: string;
affectedPaths: string[];
fileContents: Record<string, string>;
hetznerCommitSha?: string;
hetznerPushStatus: 'pending' | 'pushed' | 'failed';
}
// User weights
interface UserCategoryWeights {
userId: string;
weights: Record<SuggestionCategory, number>;
topPatterns: Array<{
componentType: string;
preferredSuggestions: string[];
}>;
updatedAt: Date;
}
12. Implementation Phases
V1 — Foundation + Core Loop
Goal: User clicks element → sees toolbar → clicks suggestion → it applies. The full loop works.
Prerequisites (P0):
- P0-A: Implement ComponentTaggingPlugin integration (2-3 days). The plugin exists at
platform-preview/src/lib/plugins/component-tagging.plugin.tsbut is not wired into the compilation pipeline.studioTaggingLoaderis never called. Implementation: integratestudioTaggingLoaderintoBundlerFilesystemService.enrichFileMap()as a template enrichment step — run it on every.component.htmlBEFOREinlineAngularComponentResources. Must work in the Web Worker-based rspack architecture. Test: every generated component's root element hasdata-studio-idin the compiled preview HTML. - P0-B: SDK Manifest Pipeline — 15
.intelligence.tsfiles, NX build target, auto-generated prompt partial. Manifest delivered to browser as a static JSON asset bundled into vitto-studio application (imported via TypeScriptimport manifestJson from './sdk-manifest.json').
Deliverables:
- V1-1: Enhanced preview bridge client (rich ElementSelectionPayload, PVI_INJECT_CSS handler)
- V1-2: SuggestionRuleEngine (~20 rules from manifest)
- V1-3: PviToolbarComponent + PviSuggestionPanelComponent (floating toolbar with category expansion)
- V1-4: Style apply engine (CSS injection via new
PreviewBridgeService.sendPviMessage()method → SCSS persistence via newStyleInjectionService.patchScssFile()utility that reads SCSS from VFS, patches string, writes back viaVfsService.updateFileContent()) - V1-5: Structural apply engine (LLM code gen → VFS write → compile, with auto-rollback)
- V1-6: Local undo (in-memory VFS snapshots, undo button)
NOT in V1: Ambient dots, chat summary, LLM enrichment, learning layer, Apply All, Hetzner git, timeline UI.
V2 — Intelligence + Learning
Goal: Vitto proactively analyzes, suggests via chat, learns preferences, persists history.
Deliverables:
- V2-1: Post-generation LLM deep scan (parallel Haiku + rule merge)
- V2-2: Ambient dots (inject into preview, survive recompile)
- V2-3: Chat summary component (ranked top-3 + Apply All)
- V2-4: On-click LLM enrichment with smart Haiku/Sonnet routing
- V2-5: Apply All with conflict detection
- V2-6: Learning layer (event tracking, weight aggregation, toolbar personalization)
- V2-7: Hetzner git snapshots + PviTimelineComponent
- V2-8: Complete .intelligence.ts coverage (all 47 components)
V3 — Mastery + Ecosystem
Goal: Self-improving system. Vitto reasons across modules, learns from all users, evolves the generation pipeline.
Deliverables:
- V3-1: Opus chat escalation with cross-module reasoning
- V3-2: Feedback quality loop (undo tracking, compilation success rates)
- V3-3: Auto-evolving SDK manifest (learning data → .intelligence.ts updates)
- V3-4: Generation pipeline feedback (PVI data → better implement.prompt.md)
- V3-5: Multi-element selection (Shift+click, bulk apply, shared component extraction)
- V3-6: Design system evolution (new theme tokens from usage patterns)
- V3-7: Team-level learning (org aggregate patterns, new user onboarding)
13. Critical Dependencies and Risks
| Risk | Severity | Mitigation |
|---|---|---|
| ComponentTaggingPlugin incomplete | Blocking V1 | Verify first. If broken, fix before any PVI work. |
| Dots lost on preview recompile | Medium | Re-render from stored map on iframe load event, anchor by data-studio-id |
| SCSS persistence complexity | Medium | String-based patching on predictable BEM structure. No AST parser needed for generated code. |
| Hetzner git latency for undo | Medium | Local-first snapshots. Hetzner push is async, fire-and-forget. |
| LLM cost on rapid clicking | Low | 500ms debounce + cancel in-flight + cache per component:category |
| Apply All conflicts | Low | Explicit conflicts[] on each suggestion. Skip and report. |
| Structural apply compilation failure | Medium | Auto-rollback from pre-apply snapshot. User never sees broken state. |
| Learning cold start | Low | Sensible defaults: style=0.30, layout=0.25, component=0.20, ux=0.15, data=0.05, a11y=0.05 |
| LLM generates broken code on apply | Medium | Auto-rollback + error badge. Track failure rate per suggestion type. |
| sdk-manifest.json staleness | Low | Generated by NX build target. CI validates manifest matches component source. |
14. Cost Model
| Action | Model | Frequency | Cost per Session |
|---|---|---|---|
| Post-gen scan (15 components) | Haiku (parallel) | 1x per generation | $0.02-0.04 |
| On-click enrichment | Haiku or Sonnet | ~10 clicks | $0.01-0.05 |
| Structural apply | Sonnet | ~3 applies | $0.03-0.09 |
| Chat escalation | Opus | ~1 per session | $0.05-0.10 |
| Total per editing session | $0.11-0.28 |
15. Success Metrics
| Metric | V1 Target | V2 Target | V3 Target |
|---|---|---|---|
| Suggestion apply rate | ≥20% | ≥35% | ≥50% |
| Style apply latency | <100ms | <100ms | <100ms |
| Structural apply success rate | ≥70% | ≥85% | ≥95% |
| Post-gen analysis time | N/A | <5s | <3s |
| User undo rate (quality signal) | <30% | <20% | <15% |
| Suggestions per platform | ≥5 | ≥10 (LLM-enriched) | ≥15 (personalized) |
16. Related Sub-Projects (Future Specs)
This spec is the first of three:
- Proactive Visual Intelligence (this spec) — Click → suggest → apply → learn
- Generation Pipeline Quality — Prompt engineering, auto-correction, validation, sdk-manifest integration into generation
- Platform Editability — Code structure improvements that make generated code easier to modify manually
Sub-projects 2 and 3 will be designed and specced separately, building on the manifest and learning infrastructure established here.