active_canvas 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +318 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/active_canvas/editor/ai_panel.js +1607 -0
- data/app/assets/javascripts/active_canvas/editor/asset_manager.js +498 -0
- data/app/assets/javascripts/active_canvas/editor/blocks.js +1083 -0
- data/app/assets/javascripts/active_canvas/editor/code_panel.js +572 -0
- data/app/assets/javascripts/active_canvas/editor/component_toolbar.js +394 -0
- data/app/assets/javascripts/active_canvas/editor/panels.js +460 -0
- data/app/assets/javascripts/active_canvas/editor/utils.js +56 -0
- data/app/assets/javascripts/active_canvas/editor.js +295 -0
- data/app/assets/stylesheets/active_canvas/application.css +15 -0
- data/app/assets/stylesheets/active_canvas/editor.css +2929 -0
- data/app/controllers/active_canvas/admin/ai_controller.rb +181 -0
- data/app/controllers/active_canvas/admin/application_controller.rb +56 -0
- data/app/controllers/active_canvas/admin/media_controller.rb +61 -0
- data/app/controllers/active_canvas/admin/page_types_controller.rb +57 -0
- data/app/controllers/active_canvas/admin/page_versions_controller.rb +23 -0
- data/app/controllers/active_canvas/admin/pages_controller.rb +133 -0
- data/app/controllers/active_canvas/admin/partials_controller.rb +88 -0
- data/app/controllers/active_canvas/admin/settings_controller.rb +256 -0
- data/app/controllers/active_canvas/application_controller.rb +20 -0
- data/app/controllers/active_canvas/pages_controller.rb +18 -0
- data/app/controllers/concerns/active_canvas/current_user.rb +12 -0
- data/app/controllers/concerns/active_canvas/rate_limitable.rb +75 -0
- data/app/controllers/concerns/active_canvas/tailwind_compilation.rb +39 -0
- data/app/helpers/active_canvas/application_helper.rb +4 -0
- data/app/jobs/active_canvas/application_job.rb +4 -0
- data/app/jobs/active_canvas/compile_tailwind_job.rb +64 -0
- data/app/mailers/active_canvas/application_mailer.rb +6 -0
- data/app/models/active_canvas/ai_model.rb +136 -0
- data/app/models/active_canvas/application_record.rb +5 -0
- data/app/models/active_canvas/media.rb +141 -0
- data/app/models/active_canvas/page.rb +85 -0
- data/app/models/active_canvas/page_type.rb +22 -0
- data/app/models/active_canvas/page_version.rb +80 -0
- data/app/models/active_canvas/partial.rb +73 -0
- data/app/models/active_canvas/setting.rb +292 -0
- data/app/services/active_canvas/ai_configuration.rb +40 -0
- data/app/services/active_canvas/ai_models.rb +128 -0
- data/app/services/active_canvas/ai_service.rb +289 -0
- data/app/services/active_canvas/content_sanitizer.rb +112 -0
- data/app/services/active_canvas/tailwind_compiler.rb +156 -0
- data/app/views/active_canvas/admin/media/index.html.erb +401 -0
- data/app/views/active_canvas/admin/media/show.html.erb +297 -0
- data/app/views/active_canvas/admin/page_types/_form.html.erb +25 -0
- data/app/views/active_canvas/admin/page_types/edit.html.erb +13 -0
- data/app/views/active_canvas/admin/page_types/index.html.erb +29 -0
- data/app/views/active_canvas/admin/page_types/new.html.erb +9 -0
- data/app/views/active_canvas/admin/page_types/show.html.erb +18 -0
- data/app/views/active_canvas/admin/page_versions/show.html.erb +469 -0
- data/app/views/active_canvas/admin/pages/_form.html.erb +62 -0
- data/app/views/active_canvas/admin/pages/content.html.erb +139 -0
- data/app/views/active_canvas/admin/pages/edit.html.erb +335 -0
- data/app/views/active_canvas/admin/pages/editor.html.erb +710 -0
- data/app/views/active_canvas/admin/pages/index.html.erb +149 -0
- data/app/views/active_canvas/admin/pages/new.html.erb +19 -0
- data/app/views/active_canvas/admin/pages/show.html.erb +258 -0
- data/app/views/active_canvas/admin/pages/versions.html.erb +333 -0
- data/app/views/active_canvas/admin/partials/edit.html.erb +182 -0
- data/app/views/active_canvas/admin/partials/editor.html.erb +703 -0
- data/app/views/active_canvas/admin/partials/index.html.erb +131 -0
- data/app/views/active_canvas/admin/settings/show.html.erb +1864 -0
- data/app/views/active_canvas/pages/no_homepage.html.erb +45 -0
- data/app/views/active_canvas/pages/show.html.erb +113 -0
- data/app/views/layouts/active_canvas/admin/application.html.erb +960 -0
- data/app/views/layouts/active_canvas/admin/editor.html.erb +826 -0
- data/app/views/layouts/active_canvas/application.html.erb +55 -0
- data/config/routes.rb +48 -0
- data/db/migrate/20260202000001_create_active_canvas_tables.rb +113 -0
- data/db/migrate/20260202000002_create_active_canvas_ai_models.rb +26 -0
- data/lib/active_canvas/configuration.rb +232 -0
- data/lib/active_canvas/engine.rb +44 -0
- data/lib/active_canvas/version.rb +3 -0
- data/lib/active_canvas.rb +26 -0
- data/lib/generators/active_canvas/install/install_generator.rb +263 -0
- data/lib/generators/active_canvas/install/templates/initializer.rb.tt +163 -0
- data/lib/tasks/active_canvas_tasks.rake +69 -0
- metadata +150 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
<!-- Editor Header -->
|
|
2
|
+
<header class="editor-header">
|
|
3
|
+
<div class="editor-header-left">
|
|
4
|
+
<%= link_to admin_pages_path, class: "editor-logo" do %>
|
|
5
|
+
<div class="editor-logo-icon">
|
|
6
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
7
|
+
<polygon points="12 2 2 7 12 12 22 7 12 2"/>
|
|
8
|
+
<polyline points="2 17 12 22 22 17"/>
|
|
9
|
+
<polyline points="2 12 12 17 22 12"/>
|
|
10
|
+
</svg>
|
|
11
|
+
</div>
|
|
12
|
+
<% end %>
|
|
13
|
+
<%= link_to admin_page_path(@page), class: "editor-back" do %>
|
|
14
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
15
|
+
<path d="m15 18-6-6 6-6"/>
|
|
16
|
+
</svg>
|
|
17
|
+
Back
|
|
18
|
+
<% end %>
|
|
19
|
+
<div class="header-separator"></div>
|
|
20
|
+
<div class="editor-title"><%= @page.title %></div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="editor-header-center">
|
|
24
|
+
<button class="device-btn active" data-device="desktop" title="Desktop">
|
|
25
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
26
|
+
<rect x="2" y="3" width="20" height="14" rx="2" ry="2"/>
|
|
27
|
+
<line x1="8" y1="21" x2="16" y2="21"/>
|
|
28
|
+
<line x1="12" y1="17" x2="12" y2="21"/>
|
|
29
|
+
</svg>
|
|
30
|
+
</button>
|
|
31
|
+
<button class="device-btn" data-device="tablet" title="Tablet">
|
|
32
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
33
|
+
<rect x="4" y="2" width="16" height="20" rx="2" ry="2"/>
|
|
34
|
+
<line x1="12" y1="18" x2="12.01" y2="18"/>
|
|
35
|
+
</svg>
|
|
36
|
+
</button>
|
|
37
|
+
<button class="device-btn" data-device="mobile" title="Mobile">
|
|
38
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
39
|
+
<rect x="5" y="2" width="14" height="20" rx="2" ry="2"/>
|
|
40
|
+
<line x1="12" y1="18" x2="12.01" y2="18"/>
|
|
41
|
+
</svg>
|
|
42
|
+
</button>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="editor-header-right">
|
|
46
|
+
<button class="header-btn header-btn-secondary" id="btn-toggle-left" title="Toggle Blocks Panel">
|
|
47
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
48
|
+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
|
49
|
+
<line x1="9" y1="3" x2="9" y2="21"/>
|
|
50
|
+
</svg>
|
|
51
|
+
</button>
|
|
52
|
+
<button class="header-btn header-btn-secondary" id="btn-toggle-ai" title="Toggle AI Assistant">
|
|
53
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
54
|
+
<path d="M12 8V4H8"/>
|
|
55
|
+
<rect width="16" height="12" x="4" y="8" rx="2"/>
|
|
56
|
+
<path d="M2 14h2"/>
|
|
57
|
+
<path d="M20 14h2"/>
|
|
58
|
+
<path d="M15 13v2"/>
|
|
59
|
+
<path d="M9 13v2"/>
|
|
60
|
+
</svg>
|
|
61
|
+
</button>
|
|
62
|
+
<button class="header-btn header-btn-secondary" id="btn-toggle-right" title="Toggle Styles Panel">
|
|
63
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
64
|
+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
|
65
|
+
<line x1="15" y1="3" x2="15" y2="21"/>
|
|
66
|
+
</svg>
|
|
67
|
+
</button>
|
|
68
|
+
<div class="header-separator"></div>
|
|
69
|
+
<button class="header-btn header-btn-secondary" id="btn-undo" title="Undo (Ctrl+Z)">
|
|
70
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
71
|
+
<path d="M3 7v6h6"/>
|
|
72
|
+
<path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/>
|
|
73
|
+
</svg>
|
|
74
|
+
</button>
|
|
75
|
+
<button class="header-btn header-btn-secondary" id="btn-redo" title="Redo (Ctrl+Y)">
|
|
76
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
77
|
+
<path d="M21 7v6h-6"/>
|
|
78
|
+
<path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"/>
|
|
79
|
+
</svg>
|
|
80
|
+
</button>
|
|
81
|
+
<button class="header-btn header-btn-secondary" id="btn-code" title="View Code (Ctrl+E)">
|
|
82
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
83
|
+
<polyline points="16 18 22 12 16 6"/>
|
|
84
|
+
<polyline points="8 6 2 12 8 18"/>
|
|
85
|
+
</svg>
|
|
86
|
+
Code
|
|
87
|
+
</button>
|
|
88
|
+
<div class="theme-switcher">
|
|
89
|
+
<button class="header-btn header-btn-secondary" id="btn-theme" title="Change Theme">
|
|
90
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
91
|
+
<circle cx="12" cy="12" r="5"/>
|
|
92
|
+
<line x1="12" y1="1" x2="12" y2="3"/>
|
|
93
|
+
<line x1="12" y1="21" x2="12" y2="23"/>
|
|
94
|
+
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
|
|
95
|
+
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
|
96
|
+
<line x1="1" y1="12" x2="3" y2="12"/>
|
|
97
|
+
<line x1="21" y1="12" x2="23" y2="12"/>
|
|
98
|
+
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
|
|
99
|
+
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
|
100
|
+
</svg>
|
|
101
|
+
</button>
|
|
102
|
+
<div class="theme-dropdown" id="theme-dropdown">
|
|
103
|
+
<div class="theme-dropdown-header">Editor Theme</div>
|
|
104
|
+
<button class="theme-option" data-theme="dark">
|
|
105
|
+
<span class="theme-preview theme-preview-dark"></span>
|
|
106
|
+
<span class="theme-name">Dark</span>
|
|
107
|
+
<span class="theme-check">
|
|
108
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
109
|
+
<polyline points="20 6 9 17 4 12"/>
|
|
110
|
+
</svg>
|
|
111
|
+
</span>
|
|
112
|
+
</button>
|
|
113
|
+
<button class="theme-option" data-theme="midnight">
|
|
114
|
+
<span class="theme-preview theme-preview-midnight"></span>
|
|
115
|
+
<span class="theme-name">Midnight</span>
|
|
116
|
+
<span class="theme-check">
|
|
117
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
118
|
+
<polyline points="20 6 9 17 4 12"/>
|
|
119
|
+
</svg>
|
|
120
|
+
</span>
|
|
121
|
+
</button>
|
|
122
|
+
<button class="theme-option" data-theme="ocean">
|
|
123
|
+
<span class="theme-preview theme-preview-ocean"></span>
|
|
124
|
+
<span class="theme-name">Ocean</span>
|
|
125
|
+
<span class="theme-check">
|
|
126
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
127
|
+
<polyline points="20 6 9 17 4 12"/>
|
|
128
|
+
</svg>
|
|
129
|
+
</span>
|
|
130
|
+
</button>
|
|
131
|
+
<button class="theme-option" data-theme="charcoal">
|
|
132
|
+
<span class="theme-preview theme-preview-charcoal"></span>
|
|
133
|
+
<span class="theme-name">Charcoal</span>
|
|
134
|
+
<span class="theme-check">
|
|
135
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
136
|
+
<polyline points="20 6 9 17 4 12"/>
|
|
137
|
+
</svg>
|
|
138
|
+
</span>
|
|
139
|
+
</button>
|
|
140
|
+
<button class="theme-option" data-theme="light">
|
|
141
|
+
<span class="theme-preview theme-preview-light"></span>
|
|
142
|
+
<span class="theme-name">Light</span>
|
|
143
|
+
<span class="theme-check">
|
|
144
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
145
|
+
<polyline points="20 6 9 17 4 12"/>
|
|
146
|
+
</svg>
|
|
147
|
+
</span>
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
150
|
+
</div>
|
|
151
|
+
<div class="header-separator"></div>
|
|
152
|
+
<button class="header-btn header-btn-primary" id="btn-save">
|
|
153
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
154
|
+
<path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/>
|
|
155
|
+
<polyline points="17 21 17 13 7 13 7 21"/>
|
|
156
|
+
<polyline points="7 3 7 8 15 8"/>
|
|
157
|
+
</svg>
|
|
158
|
+
Save
|
|
159
|
+
</button>
|
|
160
|
+
</div>
|
|
161
|
+
</header>
|
|
162
|
+
|
|
163
|
+
<!-- Main Editor -->
|
|
164
|
+
<div class="editor-wrapper">
|
|
165
|
+
<div class="editor-main">
|
|
166
|
+
<div class="editor-container">
|
|
167
|
+
<!-- Left Panel: Blocks -->
|
|
168
|
+
<div class="editor-panel-left collapsed" id="panel-left">
|
|
169
|
+
<div class="panel-tabs">
|
|
170
|
+
<button class="panel-tab active" data-panel="blocks">Blocks</button>
|
|
171
|
+
<button class="panel-tab" data-panel="assets">Assets</button>
|
|
172
|
+
<button class="panel-tab" data-panel="layers">Layers</button>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="panel-content">
|
|
175
|
+
<div id="blocks-container"></div>
|
|
176
|
+
<div id="assets-container" style="display: none;">
|
|
177
|
+
<div class="assets-panel">
|
|
178
|
+
<div class="assets-toolbar">
|
|
179
|
+
<button class="assets-upload-btn" id="btn-upload-asset" title="Upload new image">
|
|
180
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
181
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
182
|
+
<polyline points="17 8 12 3 7 8"/>
|
|
183
|
+
<line x1="12" y1="3" x2="12" y2="15"/>
|
|
184
|
+
</svg>
|
|
185
|
+
Upload
|
|
186
|
+
</button>
|
|
187
|
+
<button class="assets-refresh-btn" id="btn-refresh-assets" title="Refresh assets">
|
|
188
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
189
|
+
<polyline points="23 4 23 10 17 10"/>
|
|
190
|
+
<polyline points="1 20 1 14 7 14"/>
|
|
191
|
+
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
|
|
192
|
+
</svg>
|
|
193
|
+
</button>
|
|
194
|
+
</div>
|
|
195
|
+
<div class="assets-grid" id="assets-grid">
|
|
196
|
+
<div class="assets-loading">Loading assets...</div>
|
|
197
|
+
</div>
|
|
198
|
+
<input type="file" id="asset-upload-input" accept="image/*" multiple style="display: none;">
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
<div id="layers-container" style="display: none;"></div>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
|
|
205
|
+
<!-- AI Panel (separate from left panel) -->
|
|
206
|
+
<div class="editor-panel-ai collapsed" id="panel-ai">
|
|
207
|
+
<div class="ai-panel-header">
|
|
208
|
+
<div class="ai-panel-title">
|
|
209
|
+
<div class="ai-panel-icon">
|
|
210
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
211
|
+
<path d="M12 8V4H8"/>
|
|
212
|
+
<rect width="16" height="12" x="4" y="8" rx="2"/>
|
|
213
|
+
<path d="M2 14h2"/>
|
|
214
|
+
<path d="M20 14h2"/>
|
|
215
|
+
<path d="M15 13v2"/>
|
|
216
|
+
<path d="M9 13v2"/>
|
|
217
|
+
</svg>
|
|
218
|
+
</div>
|
|
219
|
+
<div class="ai-panel-title-text">
|
|
220
|
+
<span>AI Assistant</span>
|
|
221
|
+
<span class="ai-status-indicator" id="ai-status-badge"></span>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
<button class="ai-panel-close" id="btn-close-ai" title="Close panel">
|
|
225
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
226
|
+
<line x1="18" y1="6" x2="6" y2="18"/>
|
|
227
|
+
<line x1="6" y1="6" x2="18" y2="18"/>
|
|
228
|
+
</svg>
|
|
229
|
+
</button>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<div class="ai-panel-body">
|
|
233
|
+
<!-- Not configured state -->
|
|
234
|
+
<div class="ai-empty-state" id="ai-not-configured" style="display: none;">
|
|
235
|
+
<div class="ai-empty-state-glow"></div>
|
|
236
|
+
<div class="ai-empty-state-icon">
|
|
237
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
238
|
+
<path d="M12 8V4H8"/>
|
|
239
|
+
<rect width="16" height="12" x="4" y="8" rx="2"/>
|
|
240
|
+
<path d="M2 14h2"/>
|
|
241
|
+
<path d="M20 14h2"/>
|
|
242
|
+
<path d="M15 13v2"/>
|
|
243
|
+
<path d="M9 13v2"/>
|
|
244
|
+
</svg>
|
|
245
|
+
</div>
|
|
246
|
+
<h3>AI Not Configured</h3>
|
|
247
|
+
<p>Connect your API keys to unlock AI-powered content generation.</p>
|
|
248
|
+
<%= link_to admin_settings_path(tab: "ai"), class: "ai-empty-state-btn" do %>
|
|
249
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
250
|
+
<circle cx="12" cy="12" r="3"/>
|
|
251
|
+
<path d="M12 1v6m0 6v10"/>
|
|
252
|
+
<path d="m4.93 4.93 4.24 4.24m5.66 5.66 4.24 4.24"/>
|
|
253
|
+
<path d="M1 12h6m6 0h10"/>
|
|
254
|
+
<path d="m4.93 19.07 4.24-4.24m5.66-5.66 4.24-4.24"/>
|
|
255
|
+
</svg>
|
|
256
|
+
Configure AI
|
|
257
|
+
<% end %>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
<!-- Feature Tabs -->
|
|
261
|
+
<div class="ai-tabs" id="ai-panel-tabs">
|
|
262
|
+
<button class="ai-tab active" data-tab="text">
|
|
263
|
+
<span class="ai-tab-icon">
|
|
264
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
265
|
+
<path d="M17 6.1H3"/>
|
|
266
|
+
<path d="M21 12.1H3"/>
|
|
267
|
+
<path d="M15.1 18H3"/>
|
|
268
|
+
</svg>
|
|
269
|
+
</span>
|
|
270
|
+
<span class="ai-tab-label">Generate</span>
|
|
271
|
+
</button>
|
|
272
|
+
<button class="ai-tab" data-tab="image">
|
|
273
|
+
<span class="ai-tab-icon">
|
|
274
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
275
|
+
<rect width="18" height="18" x="3" y="3" rx="2" ry="2"/>
|
|
276
|
+
<circle cx="9" cy="9" r="2"/>
|
|
277
|
+
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/>
|
|
278
|
+
</svg>
|
|
279
|
+
</span>
|
|
280
|
+
<span class="ai-tab-label">Image</span>
|
|
281
|
+
</button>
|
|
282
|
+
<button class="ai-tab" data-tab="screenshot">
|
|
283
|
+
<span class="ai-tab-icon">
|
|
284
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
285
|
+
<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/>
|
|
286
|
+
<circle cx="12" cy="13" r="3"/>
|
|
287
|
+
</svg>
|
|
288
|
+
</span>
|
|
289
|
+
<span class="ai-tab-label">Screenshot</span>
|
|
290
|
+
</button>
|
|
291
|
+
</div>
|
|
292
|
+
|
|
293
|
+
<!-- Text Generation Tab -->
|
|
294
|
+
<div class="ai-tab-panel active" data-tab="text">
|
|
295
|
+
<!-- Mode Toggle -->
|
|
296
|
+
<div class="ai-mode-toggle" id="ai-mode-selector">
|
|
297
|
+
<button class="ai-mode-btn active" data-mode="page">
|
|
298
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
299
|
+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
|
300
|
+
<line x1="3" y1="9" x2="21" y2="9"/>
|
|
301
|
+
</svg>
|
|
302
|
+
Page
|
|
303
|
+
</button>
|
|
304
|
+
<button class="ai-mode-btn" data-mode="element">
|
|
305
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
306
|
+
<path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z"/>
|
|
307
|
+
<path d="M13 13l6 6"/>
|
|
308
|
+
</svg>
|
|
309
|
+
Element
|
|
310
|
+
</button>
|
|
311
|
+
<div class="ai-mode-slider"></div>
|
|
312
|
+
</div>
|
|
313
|
+
|
|
314
|
+
<div class="ai-element-indicator" id="ai-element-info" style="display: none;">
|
|
315
|
+
<span class="ai-element-label">Editing:</span>
|
|
316
|
+
<code id="ai-selected-element">No element selected</code>
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<!-- Model Selector -->
|
|
320
|
+
<div class="ai-model-row">
|
|
321
|
+
<label class="ai-model-label">Model</label>
|
|
322
|
+
<div class="ai-model-picker" id="ai-text-model-picker" data-tab="text">
|
|
323
|
+
<button type="button" class="ai-model-picker-btn" aria-haspopup="listbox">
|
|
324
|
+
<span class="ai-model-picker-label">Loading...</span>
|
|
325
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ai-select-chevron"><path d="m6 9 6 6 6-6"/></svg>
|
|
326
|
+
</button>
|
|
327
|
+
<div class="ai-model-picker-dropdown">
|
|
328
|
+
<input type="text" class="ai-model-picker-search" placeholder="Search models..." autocomplete="off" />
|
|
329
|
+
<ul class="ai-model-picker-list" role="listbox"></ul>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<!-- Conversation Area -->
|
|
335
|
+
<div class="ai-conversation" id="ai-text-conversation">
|
|
336
|
+
<div class="ai-conversation-empty">
|
|
337
|
+
<div class="ai-conversation-empty-icon">
|
|
338
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
339
|
+
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
|
|
340
|
+
</svg>
|
|
341
|
+
</div>
|
|
342
|
+
<p>Describe what you want to create</p>
|
|
343
|
+
</div>
|
|
344
|
+
</div>
|
|
345
|
+
|
|
346
|
+
<!-- Response Output -->
|
|
347
|
+
<div class="ai-response-area" id="ai-text-output-wrapper" style="display: none;">
|
|
348
|
+
<div class="ai-response-header">
|
|
349
|
+
<div class="ai-response-avatar">
|
|
350
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
351
|
+
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>
|
|
352
|
+
</svg>
|
|
353
|
+
</div>
|
|
354
|
+
<span>AI Response</span>
|
|
355
|
+
<button class="ai-copy-btn" id="btn-ai-copy" title="Copy to clipboard">
|
|
356
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
357
|
+
<rect width="14" height="14" x="8" y="8" rx="2" ry="2"/>
|
|
358
|
+
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/>
|
|
359
|
+
</svg>
|
|
360
|
+
</button>
|
|
361
|
+
</div>
|
|
362
|
+
<div class="ai-response-content" id="ai-text-output"></div>
|
|
363
|
+
<div class="ai-response-actions">
|
|
364
|
+
<button class="ai-action-btn ai-action-primary" id="btn-ai-text-insert">
|
|
365
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
366
|
+
<polyline points="9 11 12 14 22 4"/>
|
|
367
|
+
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
|
|
368
|
+
</svg>
|
|
369
|
+
Insert to Page
|
|
370
|
+
</button>
|
|
371
|
+
<button class="ai-action-btn" id="btn-ai-regenerate">
|
|
372
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
373
|
+
<path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/>
|
|
374
|
+
<path d="M21 3v5h-5"/>
|
|
375
|
+
</svg>
|
|
376
|
+
Regenerate
|
|
377
|
+
</button>
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
|
|
381
|
+
<!-- Prompt Input -->
|
|
382
|
+
<div class="ai-prompt-container">
|
|
383
|
+
<textarea id="ai-text-prompt" class="ai-prompt-input" placeholder="Create a hero section with..." rows="1"></textarea>
|
|
384
|
+
<button class="ai-send-btn" id="btn-ai-text-generate" disabled>
|
|
385
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
386
|
+
<path d="m5 12 7-7 7 7"/>
|
|
387
|
+
<path d="M12 19V5"/>
|
|
388
|
+
</svg>
|
|
389
|
+
</button>
|
|
390
|
+
</div>
|
|
391
|
+
<div class="ai-prompt-hint">
|
|
392
|
+
<kbd>Enter</kbd> to send <span class="ai-hint-separator">|</span> <kbd>Shift+Enter</kbd> for new line
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
|
|
396
|
+
<!-- Image Generation Tab -->
|
|
397
|
+
<div class="ai-tab-panel" data-tab="image">
|
|
398
|
+
<div class="ai-model-row">
|
|
399
|
+
<label class="ai-model-label">Model</label>
|
|
400
|
+
<div class="ai-model-picker" id="ai-image-model-picker" data-tab="image">
|
|
401
|
+
<button type="button" class="ai-model-picker-btn" aria-haspopup="listbox">
|
|
402
|
+
<span class="ai-model-picker-label">Loading...</span>
|
|
403
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ai-select-chevron"><path d="m6 9 6 6 6-6"/></svg>
|
|
404
|
+
</button>
|
|
405
|
+
<div class="ai-model-picker-dropdown">
|
|
406
|
+
<input type="text" class="ai-model-picker-search" placeholder="Search models..." autocomplete="off" />
|
|
407
|
+
<ul class="ai-model-picker-list" role="listbox"></ul>
|
|
408
|
+
</div>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
|
|
412
|
+
<!-- Image Output -->
|
|
413
|
+
<div class="ai-image-output" id="ai-image-output-wrapper" style="display: none;">
|
|
414
|
+
<div class="ai-image-preview" id="ai-image-output"></div>
|
|
415
|
+
<div class="ai-response-actions">
|
|
416
|
+
<button class="ai-action-btn ai-action-primary" id="btn-ai-image-insert">
|
|
417
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
418
|
+
<polyline points="9 11 12 14 22 4"/>
|
|
419
|
+
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
|
|
420
|
+
</svg>
|
|
421
|
+
Insert Image
|
|
422
|
+
</button>
|
|
423
|
+
</div>
|
|
424
|
+
</div>
|
|
425
|
+
|
|
426
|
+
<div class="ai-image-empty" id="ai-image-empty">
|
|
427
|
+
<div class="ai-image-empty-icon">
|
|
428
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
429
|
+
<rect width="18" height="18" x="3" y="3" rx="2" ry="2"/>
|
|
430
|
+
<circle cx="9" cy="9" r="2"/>
|
|
431
|
+
<path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/>
|
|
432
|
+
</svg>
|
|
433
|
+
</div>
|
|
434
|
+
<p>Generate images with AI</p>
|
|
435
|
+
</div>
|
|
436
|
+
|
|
437
|
+
<div class="ai-prompt-container">
|
|
438
|
+
<textarea id="ai-image-prompt" class="ai-prompt-input" placeholder="A minimalist illustration of..." rows="1"></textarea>
|
|
439
|
+
<button class="ai-send-btn" id="btn-ai-image-generate" disabled>
|
|
440
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
441
|
+
<path d="m5 12 7-7 7 7"/>
|
|
442
|
+
<path d="M12 19V5"/>
|
|
443
|
+
</svg>
|
|
444
|
+
</button>
|
|
445
|
+
</div>
|
|
446
|
+
</div>
|
|
447
|
+
|
|
448
|
+
<!-- Screenshot to Code Tab -->
|
|
449
|
+
<div class="ai-tab-panel" data-tab="screenshot">
|
|
450
|
+
<div class="ai-upload-zone" id="ai-screenshot-drop">
|
|
451
|
+
<input type="file" id="ai-screenshot-input" accept="image/*" class="ai-upload-input" />
|
|
452
|
+
<div class="ai-upload-content" id="ai-upload-placeholder">
|
|
453
|
+
<div class="ai-upload-icon">
|
|
454
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
|
|
455
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
456
|
+
<polyline points="17 8 12 3 7 8"/>
|
|
457
|
+
<line x1="12" y1="3" x2="12" y2="15"/>
|
|
458
|
+
</svg>
|
|
459
|
+
</div>
|
|
460
|
+
<span class="ai-upload-text">Drop screenshot here</span>
|
|
461
|
+
<span class="ai-upload-subtext">or click to browse</span>
|
|
462
|
+
</div>
|
|
463
|
+
<div class="ai-upload-preview" id="ai-screenshot-preview"></div>
|
|
464
|
+
</div>
|
|
465
|
+
|
|
466
|
+
<div class="ai-screenshot-output" id="ai-screenshot-output-wrapper" style="display: none;">
|
|
467
|
+
<div class="ai-response-header">
|
|
468
|
+
<div class="ai-response-avatar">
|
|
469
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
470
|
+
<polyline points="16 18 22 12 16 6"/>
|
|
471
|
+
<polyline points="8 6 2 12 8 18"/>
|
|
472
|
+
</svg>
|
|
473
|
+
</div>
|
|
474
|
+
<span>Generated Code</span>
|
|
475
|
+
</div>
|
|
476
|
+
<div class="ai-response-content" id="ai-screenshot-output"></div>
|
|
477
|
+
<div class="ai-response-actions">
|
|
478
|
+
<button class="ai-action-btn ai-action-primary" id="btn-ai-screenshot-insert">
|
|
479
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
480
|
+
<polyline points="9 11 12 14 22 4"/>
|
|
481
|
+
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/>
|
|
482
|
+
</svg>
|
|
483
|
+
Insert to Page
|
|
484
|
+
</button>
|
|
485
|
+
</div>
|
|
486
|
+
</div>
|
|
487
|
+
|
|
488
|
+
<div class="ai-model-row">
|
|
489
|
+
<label class="ai-model-label">Model</label>
|
|
490
|
+
<div class="ai-model-picker" id="ai-screenshot-model-picker" data-tab="screenshot">
|
|
491
|
+
<button type="button" class="ai-model-picker-btn" aria-haspopup="listbox">
|
|
492
|
+
<span class="ai-model-picker-label">Loading...</span>
|
|
493
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="ai-select-chevron"><path d="m6 9 6 6 6-6"/></svg>
|
|
494
|
+
</button>
|
|
495
|
+
<div class="ai-model-picker-dropdown">
|
|
496
|
+
<input type="text" class="ai-model-picker-search" placeholder="Search models..." autocomplete="off" />
|
|
497
|
+
<ul class="ai-model-picker-list" role="listbox"></ul>
|
|
498
|
+
</div>
|
|
499
|
+
</div>
|
|
500
|
+
</div>
|
|
501
|
+
|
|
502
|
+
<div class="ai-prompt-container">
|
|
503
|
+
<textarea id="ai-screenshot-prompt" class="ai-prompt-input" placeholder="Additional instructions (optional)..." rows="1"></textarea>
|
|
504
|
+
<button class="ai-send-btn" id="btn-ai-screenshot-convert" disabled>
|
|
505
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
506
|
+
<polyline points="16 18 22 12 16 6"/>
|
|
507
|
+
<polyline points="8 6 2 12 8 18"/>
|
|
508
|
+
</svg>
|
|
509
|
+
</button>
|
|
510
|
+
</div>
|
|
511
|
+
</div>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
|
|
515
|
+
<!-- Canvas -->
|
|
516
|
+
<div class="editor-canvas">
|
|
517
|
+
<div id="gjs"></div>
|
|
518
|
+
<button class="add-section-btn" id="btn-add-section" title="Add a new section">
|
|
519
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
520
|
+
<line x1="12" y1="5" x2="12" y2="19"/>
|
|
521
|
+
<line x1="5" y1="12" x2="19" y2="12"/>
|
|
522
|
+
</svg>
|
|
523
|
+
Add Section
|
|
524
|
+
</button>
|
|
525
|
+
</div>
|
|
526
|
+
|
|
527
|
+
<!-- Right Panel: Styles/Settings -->
|
|
528
|
+
<div class="editor-panel-right collapsed" id="panel-right">
|
|
529
|
+
<div class="panel-tabs">
|
|
530
|
+
<button class="panel-tab active" data-panel="styles">Styles</button>
|
|
531
|
+
<button class="panel-tab" data-panel="settings">Settings</button>
|
|
532
|
+
</div>
|
|
533
|
+
<div class="panel-content">
|
|
534
|
+
<div id="styles-container"></div>
|
|
535
|
+
<div id="traits-container" style="display: none;"></div>
|
|
536
|
+
</div>
|
|
537
|
+
</div>
|
|
538
|
+
</div>
|
|
539
|
+
</div>
|
|
540
|
+
|
|
541
|
+
<!-- Bottom Panel: Code Editor -->
|
|
542
|
+
<div class="editor-code-panel" id="code-panel">
|
|
543
|
+
<div class="code-panel-header">
|
|
544
|
+
<div class="code-panel-tabs">
|
|
545
|
+
<button class="code-panel-tab active" data-type="html">HTML</button>
|
|
546
|
+
<button class="code-panel-tab" data-type="css">CSS</button>
|
|
547
|
+
<button class="code-panel-tab" data-type="js">JavaScript</button>
|
|
548
|
+
</div>
|
|
549
|
+
<div class="code-panel-mode" id="code-panel-mode" style="display: none;">
|
|
550
|
+
<span class="mode-badge">
|
|
551
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
552
|
+
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
|
|
553
|
+
</svg>
|
|
554
|
+
<span id="component-name">Component</span>
|
|
555
|
+
</span>
|
|
556
|
+
<button class="code-panel-btn" id="code-full-page" title="Switch to full page">Full Page</button>
|
|
557
|
+
</div>
|
|
558
|
+
<div class="code-panel-actions">
|
|
559
|
+
<span id="code-status" class="code-status synced">Synced</span>
|
|
560
|
+
<button class="code-panel-btn" id="code-format" title="Format (Ctrl+Shift+F)">Format</button>
|
|
561
|
+
<button class="code-panel-btn primary" id="code-apply" title="Apply (Ctrl+S)">Apply</button>
|
|
562
|
+
<button class="code-panel-btn" id="code-close" title="Close">
|
|
563
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
564
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
565
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
566
|
+
</svg>
|
|
567
|
+
</button>
|
|
568
|
+
</div>
|
|
569
|
+
</div>
|
|
570
|
+
<div class="code-panel-resize" id="code-panel-resize"></div>
|
|
571
|
+
<div class="code-panel-content">
|
|
572
|
+
<div id="monaco-html-container" class="monaco-container"></div>
|
|
573
|
+
<div id="monaco-css-container" class="monaco-container" style="display: none;"></div>
|
|
574
|
+
<div id="monaco-js-container" class="monaco-container" style="display: none;"></div>
|
|
575
|
+
</div>
|
|
576
|
+
</div>
|
|
577
|
+
</div>
|
|
578
|
+
|
|
579
|
+
<!-- Toast Notification -->
|
|
580
|
+
<div id="editor-toast" class="editor-toast"></div>
|
|
581
|
+
|
|
582
|
+
<!-- Loading Overlay -->
|
|
583
|
+
<div id="editor-loading" class="editor-loading hidden">
|
|
584
|
+
<div class="editor-loading-spinner"></div>
|
|
585
|
+
</div>
|
|
586
|
+
|
|
587
|
+
<%= stylesheet_link_tag "active_canvas/editor", media: "all" %>
|
|
588
|
+
|
|
589
|
+
<!-- Theme Initialization (runs immediately to prevent flash) -->
|
|
590
|
+
<script>
|
|
591
|
+
(function() {
|
|
592
|
+
const savedTheme = localStorage.getItem('activecanvas-editor-theme') || 'dark';
|
|
593
|
+
document.documentElement.setAttribute('data-theme', savedTheme);
|
|
594
|
+
})();
|
|
595
|
+
</script>
|
|
596
|
+
|
|
597
|
+
<!-- Editor Configuration -->
|
|
598
|
+
<script>
|
|
599
|
+
window.ActiveCanvasEditor = {
|
|
600
|
+
config: {
|
|
601
|
+
pageId: <%= @page.id %>,
|
|
602
|
+
saveUrl: '<%= save_editor_admin_page_path(@page) %>',
|
|
603
|
+
mediaUrl: '<%= admin_media_path(format: :json) %>',
|
|
604
|
+
uploadUrl: '<%= admin_media_path %>',
|
|
605
|
+
content: <%= raw (@page.content || '').to_json %>,
|
|
606
|
+
contentCss: <%= raw (@page.content_css || '').to_json %>,
|
|
607
|
+
contentJs: <%= raw (@page.content_js || '').to_json %>,
|
|
608
|
+
contentComponents: <%= raw (@page.content_components || '').to_json %>,
|
|
609
|
+
cssFramework: '<%= ActiveCanvas::Setting.css_framework %>',
|
|
610
|
+
cssFrameworkUrl: <%= raw (ActiveCanvas::Setting.css_framework_url || '').to_json %>,
|
|
611
|
+
cssFrameworkType: '<%= ActiveCanvas::Setting.css_framework_type %>',
|
|
612
|
+
tailwindConfig: <%= raw ActiveCanvas::Setting.tailwind_config_js %>,
|
|
613
|
+
globalCss: <%= raw (ActiveCanvas::Setting.global_css || '').to_json %>,
|
|
614
|
+
globalJs: <%= raw (ActiveCanvas::Setting.global_js || '').to_json %>,
|
|
615
|
+
aiConnectionMode: '<%= ActiveCanvas::Setting.ai_connection_mode %>',
|
|
616
|
+
<% if ActiveCanvas::Setting.ai_connection_mode == "direct" %>
|
|
617
|
+
aiApiKeys: {
|
|
618
|
+
openai: <%= raw ActiveCanvas::Setting.ai_openai_api_key.to_json %>,
|
|
619
|
+
anthropic: <%= raw ActiveCanvas::Setting.ai_anthropic_api_key.to_json %>,
|
|
620
|
+
openrouter: <%= raw ActiveCanvas::Setting.ai_openrouter_api_key.to_json %>
|
|
621
|
+
},
|
|
622
|
+
<% end %>
|
|
623
|
+
aiEndpoints: {
|
|
624
|
+
status: '<%= admin_ai_status_path %>',
|
|
625
|
+
models: '<%= admin_ai_models_path %>',
|
|
626
|
+
chat: '<%= admin_ai_chat_path %>',
|
|
627
|
+
image: '<%= admin_ai_image_path %>',
|
|
628
|
+
screenshot: '<%= admin_ai_screenshot_to_code_path %>'
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
</script>
|
|
633
|
+
|
|
634
|
+
<!-- Editor JavaScript Modules (order matters) -->
|
|
635
|
+
<%= javascript_include_tag "active_canvas/editor/utils" %>
|
|
636
|
+
<%= javascript_include_tag "active_canvas/editor/blocks" %>
|
|
637
|
+
<%= javascript_include_tag "active_canvas/editor/asset_manager" %>
|
|
638
|
+
<%= javascript_include_tag "active_canvas/editor/code_panel" %>
|
|
639
|
+
<%= javascript_include_tag "active_canvas/editor/component_toolbar" %>
|
|
640
|
+
<%= javascript_include_tag "active_canvas/editor/panels" %>
|
|
641
|
+
<%= javascript_include_tag "active_canvas/editor/ai_panel" %>
|
|
642
|
+
<%= javascript_include_tag "active_canvas/editor" %>
|
|
643
|
+
|
|
644
|
+
<!-- Theme Switcher JavaScript -->
|
|
645
|
+
<script>
|
|
646
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
647
|
+
// ==================== Theme Switcher ====================
|
|
648
|
+
const themeBtn = document.getElementById('btn-theme');
|
|
649
|
+
const themeDropdown = document.getElementById('theme-dropdown');
|
|
650
|
+
const themeOptions = document.querySelectorAll('.theme-option');
|
|
651
|
+
const THEME_STORAGE_KEY = 'activecanvas-editor-theme';
|
|
652
|
+
|
|
653
|
+
// Get current theme
|
|
654
|
+
function getCurrentTheme() {
|
|
655
|
+
return localStorage.getItem(THEME_STORAGE_KEY) || 'dark';
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Apply theme
|
|
659
|
+
function applyTheme(theme) {
|
|
660
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
661
|
+
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
662
|
+
|
|
663
|
+
// Update active state in dropdown
|
|
664
|
+
themeOptions.forEach(option => {
|
|
665
|
+
option.classList.toggle('active', option.dataset.theme === theme);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
// Update Monaco editors if they exist
|
|
669
|
+
if (window.ActiveCanvasEditor && window.ActiveCanvasEditor.setMonacoTheme) {
|
|
670
|
+
window.ActiveCanvasEditor.setMonacoTheme(theme);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// Initialize theme on load
|
|
675
|
+
applyTheme(getCurrentTheme());
|
|
676
|
+
|
|
677
|
+
// Toggle dropdown
|
|
678
|
+
themeBtn.addEventListener('click', function(e) {
|
|
679
|
+
e.stopPropagation();
|
|
680
|
+
themeDropdown.classList.toggle('open');
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
// Close dropdown when clicking outside
|
|
684
|
+
document.addEventListener('click', function(e) {
|
|
685
|
+
if (!themeDropdown.contains(e.target) && e.target !== themeBtn) {
|
|
686
|
+
themeDropdown.classList.remove('open');
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// Handle theme selection
|
|
691
|
+
themeOptions.forEach(option => {
|
|
692
|
+
option.addEventListener('click', function() {
|
|
693
|
+
const theme = this.dataset.theme;
|
|
694
|
+
applyTheme(theme);
|
|
695
|
+
themeDropdown.classList.remove('open');
|
|
696
|
+
|
|
697
|
+
if (window.ActiveCanvasEditor && window.ActiveCanvasEditor.showToast) {
|
|
698
|
+
window.ActiveCanvasEditor.showToast(`Theme changed to ${this.querySelector('.theme-name').textContent}`, 'success');
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
|
|
703
|
+
// Close dropdown on Escape
|
|
704
|
+
document.addEventListener('keydown', function(e) {
|
|
705
|
+
if (e.key === 'Escape' && themeDropdown.classList.contains('open')) {
|
|
706
|
+
themeDropdown.classList.remove('open');
|
|
707
|
+
}
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
</script>
|