Skip to content

Commit 17c68ba

Browse files
committed
feat(visual-editor): Add visual page builder with AI layout generation
Implements a comprehensive visual page builder package inspired by Builder.io, Webflow, and WordPress Elementor. Key features include: Block System: - BlockInterface and ContainerBlockInterface contracts - BlockRegistry for managing block types - Core blocks: Container, Section, Grid, Column, Heading, Text, Button, Image, Spacer, Divider - Abstract base classes for easy block extension Visual Editor UI: - Three-panel layout (Layers, Canvas, Settings) - Drag-and-drop block manipulation - Real-time preview with responsive mode (desktop/tablet/mobile) - Layer tree navigation with expand/collapse - Context menu actions (add before/after, duplicate, delete) - Undo/redo history support AI Layout Generation: - AIProviderInterface with OpenAI and Anthropic providers - LayoutGeneratorService for generating layouts from text prompts - Pre-built templates (Landing, About, Pricing, etc.) - Style presets (Modern, Minimal, Bold, Elegant) - Generation history tracking Filament Integration: - VisualEditorPlugin for Filament panel - VisualEditorField form component - Full-screen editor modal Database: - visual_layouts table with versioning - block_templates table for saved/global blocks - ai_generation_history for tracking AI usage Configuration: - Added visual_editor config section - AI provider configuration (OPENAI_API_KEY, ANTHROPIC_API_KEY) - Custom block registration support
1 parent 5981f77 commit 17c68ba

45 files changed

Lines changed: 7158 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

config/inspirecms.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,4 +475,68 @@
475475
'available_locales' => ['en', 'fr', 'zh_CN', 'zh_TW', 'es', 'ja', 'de'],
476476
'user_preferred_locales' => ['en', 'zh_CN', 'zh_TW'],
477477
],
478+
479+
/*
480+
|--------------------------------------------------------------------------
481+
| Visual Editor Configuration
482+
|--------------------------------------------------------------------------
483+
|
484+
| Settings for the visual page builder with AI-powered layout generation.
485+
|
486+
*/
487+
'visual_editor' => [
488+
489+
/**
490+
* Whether the visual editor feature is enabled
491+
*/
492+
'enabled' => true,
493+
494+
/**
495+
* AI Provider Configuration
496+
*
497+
* Configure AI providers for layout generation.
498+
* Supported providers: 'openai', 'anthropic'
499+
*/
500+
'ai' => [
501+
'provider' => env('INSPIRECMS_AI_PROVIDER', 'anthropic'),
502+
503+
'openai' => [
504+
'api_key' => env('OPENAI_API_KEY'),
505+
'base_url' => env('OPENAI_BASE_URL', 'https://api.openai.com/v1'),
506+
'model' => env('OPENAI_MODEL', 'gpt-4-turbo-preview'),
507+
],
508+
509+
'anthropic' => [
510+
'api_key' => env('ANTHROPIC_API_KEY'),
511+
'base_url' => env('ANTHROPIC_BASE_URL', 'https://api.anthropic.com/v1'),
512+
'model' => env('ANTHROPIC_MODEL', 'claude-3-sonnet-20240229'),
513+
],
514+
],
515+
516+
/**
517+
* Additional custom block types to register
518+
*
519+
* Add your custom block classes here.
520+
* Each class must implement BlockInterface.
521+
*/
522+
'blocks' => [
523+
// \App\VisualEditor\Blocks\CustomBlock::class,
524+
],
525+
526+
/**
527+
* Block templates storage
528+
*/
529+
'block_templates' => [
530+
'disk' => 'public',
531+
'directory' => 'visual-editor/templates',
532+
],
533+
534+
/**
535+
* Media storage for visual editor
536+
*/
537+
'media' => [
538+
'disk' => 'public',
539+
'directory' => 'visual-editor',
540+
],
541+
],
478542
];
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
protected function getTablePrefix(): string
10+
{
11+
return config('inspirecms.models.table_name_prefix', 'cms_');
12+
}
13+
14+
public function up(): void
15+
{
16+
// Visual Layouts table
17+
Schema::create($this->getTablePrefix() . 'visual_layouts', function (Blueprint $table) {
18+
$table->uuid('id')->primary();
19+
$table->string('name')->nullable();
20+
$table->string('type')->default('page'); // page, template, block
21+
$table->json('layout_data'); // The actual block tree structure
22+
$table->json('settings')->nullable(); // Global layout settings
23+
$table->string('status')->default('draft'); // draft, published
24+
$table->string('version')->default('1.0');
25+
26+
// Polymorphic relationship to Content, Template, etc.
27+
$table->nullableUuidMorphs('layoutable');
28+
29+
// Theme support
30+
$table->string('theme')->nullable();
31+
32+
$table->uuid('created_by')->nullable();
33+
$table->uuid('updated_by')->nullable();
34+
35+
$table->timestamps();
36+
$table->softDeletes();
37+
38+
$table->index(['layoutable_type', 'layoutable_id']);
39+
$table->index('status');
40+
$table->index('theme');
41+
});
42+
43+
// Visual Layout Versions table (for history)
44+
Schema::create($this->getTablePrefix() . 'visual_layout_versions', function (Blueprint $table) {
45+
$table->uuid('id')->primary();
46+
$table->uuid('layout_id');
47+
$table->string('version');
48+
$table->json('layout_data');
49+
$table->json('settings')->nullable();
50+
$table->text('change_summary')->nullable();
51+
$table->uuid('created_by')->nullable();
52+
$table->timestamps();
53+
54+
$table->foreign('layout_id')
55+
->references('id')
56+
->on($this->getTablePrefix() . 'visual_layouts')
57+
->onDelete('cascade');
58+
59+
$table->index('layout_id');
60+
$table->index('version');
61+
});
62+
63+
// Block Templates table (for saved/reusable blocks)
64+
Schema::create($this->getTablePrefix() . 'block_templates', function (Blueprint $table) {
65+
$table->uuid('id')->primary();
66+
$table->string('name');
67+
$table->string('description')->nullable();
68+
$table->string('type'); // The block type
69+
$table->string('category')->nullable();
70+
$table->json('block_data'); // The block configuration
71+
$table->string('thumbnail')->nullable();
72+
$table->boolean('is_global')->default(false); // Global blocks are synced across uses
73+
$table->boolean('is_public')->default(true); // Can be used by all users
74+
$table->json('tags')->nullable();
75+
76+
$table->uuid('created_by')->nullable();
77+
$table->timestamps();
78+
$table->softDeletes();
79+
80+
$table->index('type');
81+
$table->index('category');
82+
$table->index('is_global');
83+
});
84+
85+
// AI Generation History table
86+
Schema::create($this->getTablePrefix() . 'ai_generation_history', function (Blueprint $table) {
87+
$table->uuid('id')->primary();
88+
$table->string('type'); // layout, content, image, suggestion
89+
$table->text('prompt');
90+
$table->json('context')->nullable(); // Additional context sent to AI
91+
$table->json('result'); // The AI response
92+
$table->string('provider'); // openai, anthropic, ollama
93+
$table->string('model');
94+
$table->integer('input_tokens')->nullable();
95+
$table->integer('output_tokens')->nullable();
96+
$table->integer('duration_ms')->nullable();
97+
$table->string('status')->default('success'); // success, error, pending
98+
$table->text('error_message')->nullable();
99+
100+
// Link to layout if applicable
101+
$table->uuid('layout_id')->nullable();
102+
103+
$table->uuid('user_id')->nullable();
104+
$table->timestamps();
105+
106+
$table->index('type');
107+
$table->index('provider');
108+
$table->index('status');
109+
$table->index('user_id');
110+
$table->index('created_at');
111+
});
112+
}
113+
114+
public function down(): void
115+
{
116+
Schema::dropIfExists($this->getTablePrefix() . 'ai_generation_history');
117+
Schema::dropIfExists($this->getTablePrefix() . 'block_templates');
118+
Schema::dropIfExists($this->getTablePrefix() . 'visual_layout_versions');
119+
Schema::dropIfExists($this->getTablePrefix() . 'visual_layouts');
120+
}
121+
};
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?php
2+
3+
return [
4+
'title' => 'Visual Editor',
5+
'description' => 'Build beautiful page layouts with drag and drop',
6+
7+
'categories' => [
8+
'layout' => 'Layout',
9+
'basic' => 'Basic',
10+
'media' => 'Media',
11+
'interactive' => 'Interactive',
12+
'integration' => 'Integration',
13+
'advanced' => 'Advanced',
14+
],
15+
16+
'blocks' => [
17+
'container' => [
18+
'label' => 'Container',
19+
'description' => 'A flexible container for grouping blocks',
20+
],
21+
'section' => [
22+
'label' => 'Section',
23+
'description' => 'A page section with background',
24+
],
25+
'grid' => [
26+
'label' => 'Grid',
27+
'description' => 'CSS Grid layout',
28+
],
29+
'column' => [
30+
'label' => 'Column',
31+
'description' => 'Grid or flex column',
32+
],
33+
'spacer' => [
34+
'label' => 'Spacer',
35+
'description' => 'Vertical spacing',
36+
],
37+
'divider' => [
38+
'label' => 'Divider',
39+
'description' => 'Horizontal line',
40+
],
41+
'heading' => [
42+
'label' => 'Heading',
43+
'description' => 'H1-H6 heading text',
44+
],
45+
'text' => [
46+
'label' => 'Text',
47+
'description' => 'Paragraph or rich text',
48+
],
49+
'button' => [
50+
'label' => 'Button',
51+
'description' => 'Clickable button or link',
52+
],
53+
'image' => [
54+
'label' => 'Image',
55+
'description' => 'Image with optional caption',
56+
],
57+
],
58+
59+
'actions' => [
60+
'add_before' => 'Add Block Before',
61+
'add_after' => 'Add Block After',
62+
'add_child' => 'Add Child Block',
63+
'duplicate' => 'Duplicate',
64+
'copy' => 'Copy',
65+
'paste' => 'Paste',
66+
'cut' => 'Cut',
67+
'delete' => 'Delete',
68+
'move_up' => 'Move Up',
69+
'move_down' => 'Move Down',
70+
'wrap' => 'Wrap in Container',
71+
'unwrap' => 'Unwrap',
72+
],
73+
74+
'toolbar' => [
75+
'undo' => 'Undo',
76+
'redo' => 'Redo',
77+
'save' => 'Save',
78+
'preview' => 'Preview',
79+
'desktop' => 'Desktop',
80+
'tablet' => 'Tablet',
81+
'mobile' => 'Mobile',
82+
],
83+
84+
'panels' => [
85+
'layers' => 'Layers',
86+
'blocks' => 'Blocks',
87+
'settings' => 'Settings',
88+
'design' => 'Design',
89+
'ai' => 'AI Assistant',
90+
],
91+
92+
'ai' => [
93+
'title' => 'AI Assistant',
94+
'description' => 'Generate layouts with AI',
95+
'generate' => 'Generate',
96+
'suggest' => 'Suggest',
97+
'prompt_placeholder' => 'Describe the layout you want to create...',
98+
'generating' => 'Generating...',
99+
'success' => 'Layout generated successfully',
100+
'error' => 'Failed to generate layout',
101+
'no_provider' => 'No AI provider configured',
102+
'templates' => [
103+
'landing' => 'Landing Page',
104+
'about' => 'About Page',
105+
'contact' => 'Contact Page',
106+
'blog' => 'Blog Layout',
107+
'portfolio' => 'Portfolio',
108+
'product' => 'Product Page',
109+
'pricing' => 'Pricing Page',
110+
'features' => 'Features Page',
111+
],
112+
'styles' => [
113+
'modern' => 'Modern',
114+
'minimal' => 'Minimal',
115+
'bold' => 'Bold',
116+
'elegant' => 'Elegant',
117+
'playful' => 'Playful',
118+
'corporate' => 'Corporate',
119+
],
120+
],
121+
122+
'messages' => [
123+
'block_added' => 'Block added',
124+
'block_deleted' => 'Block deleted',
125+
'block_duplicated' => 'Block duplicated',
126+
'block_copied' => 'Block copied',
127+
'block_pasted' => 'Block pasted',
128+
'layout_saved' => 'Layout saved',
129+
'cannot_delete_root' => 'Cannot delete root container',
130+
'nothing_to_paste' => 'Nothing to paste',
131+
'no_block_selected' => 'No block selected',
132+
],
133+
];

0 commit comments

Comments
 (0)