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.
Files changed (80) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +318 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/javascripts/active_canvas/editor/ai_panel.js +1607 -0
  6. data/app/assets/javascripts/active_canvas/editor/asset_manager.js +498 -0
  7. data/app/assets/javascripts/active_canvas/editor/blocks.js +1083 -0
  8. data/app/assets/javascripts/active_canvas/editor/code_panel.js +572 -0
  9. data/app/assets/javascripts/active_canvas/editor/component_toolbar.js +394 -0
  10. data/app/assets/javascripts/active_canvas/editor/panels.js +460 -0
  11. data/app/assets/javascripts/active_canvas/editor/utils.js +56 -0
  12. data/app/assets/javascripts/active_canvas/editor.js +295 -0
  13. data/app/assets/stylesheets/active_canvas/application.css +15 -0
  14. data/app/assets/stylesheets/active_canvas/editor.css +2929 -0
  15. data/app/controllers/active_canvas/admin/ai_controller.rb +181 -0
  16. data/app/controllers/active_canvas/admin/application_controller.rb +56 -0
  17. data/app/controllers/active_canvas/admin/media_controller.rb +61 -0
  18. data/app/controllers/active_canvas/admin/page_types_controller.rb +57 -0
  19. data/app/controllers/active_canvas/admin/page_versions_controller.rb +23 -0
  20. data/app/controllers/active_canvas/admin/pages_controller.rb +133 -0
  21. data/app/controllers/active_canvas/admin/partials_controller.rb +88 -0
  22. data/app/controllers/active_canvas/admin/settings_controller.rb +256 -0
  23. data/app/controllers/active_canvas/application_controller.rb +20 -0
  24. data/app/controllers/active_canvas/pages_controller.rb +18 -0
  25. data/app/controllers/concerns/active_canvas/current_user.rb +12 -0
  26. data/app/controllers/concerns/active_canvas/rate_limitable.rb +75 -0
  27. data/app/controllers/concerns/active_canvas/tailwind_compilation.rb +39 -0
  28. data/app/helpers/active_canvas/application_helper.rb +4 -0
  29. data/app/jobs/active_canvas/application_job.rb +4 -0
  30. data/app/jobs/active_canvas/compile_tailwind_job.rb +64 -0
  31. data/app/mailers/active_canvas/application_mailer.rb +6 -0
  32. data/app/models/active_canvas/ai_model.rb +136 -0
  33. data/app/models/active_canvas/application_record.rb +5 -0
  34. data/app/models/active_canvas/media.rb +141 -0
  35. data/app/models/active_canvas/page.rb +85 -0
  36. data/app/models/active_canvas/page_type.rb +22 -0
  37. data/app/models/active_canvas/page_version.rb +80 -0
  38. data/app/models/active_canvas/partial.rb +73 -0
  39. data/app/models/active_canvas/setting.rb +292 -0
  40. data/app/services/active_canvas/ai_configuration.rb +40 -0
  41. data/app/services/active_canvas/ai_models.rb +128 -0
  42. data/app/services/active_canvas/ai_service.rb +289 -0
  43. data/app/services/active_canvas/content_sanitizer.rb +112 -0
  44. data/app/services/active_canvas/tailwind_compiler.rb +156 -0
  45. data/app/views/active_canvas/admin/media/index.html.erb +401 -0
  46. data/app/views/active_canvas/admin/media/show.html.erb +297 -0
  47. data/app/views/active_canvas/admin/page_types/_form.html.erb +25 -0
  48. data/app/views/active_canvas/admin/page_types/edit.html.erb +13 -0
  49. data/app/views/active_canvas/admin/page_types/index.html.erb +29 -0
  50. data/app/views/active_canvas/admin/page_types/new.html.erb +9 -0
  51. data/app/views/active_canvas/admin/page_types/show.html.erb +18 -0
  52. data/app/views/active_canvas/admin/page_versions/show.html.erb +469 -0
  53. data/app/views/active_canvas/admin/pages/_form.html.erb +62 -0
  54. data/app/views/active_canvas/admin/pages/content.html.erb +139 -0
  55. data/app/views/active_canvas/admin/pages/edit.html.erb +335 -0
  56. data/app/views/active_canvas/admin/pages/editor.html.erb +710 -0
  57. data/app/views/active_canvas/admin/pages/index.html.erb +149 -0
  58. data/app/views/active_canvas/admin/pages/new.html.erb +19 -0
  59. data/app/views/active_canvas/admin/pages/show.html.erb +258 -0
  60. data/app/views/active_canvas/admin/pages/versions.html.erb +333 -0
  61. data/app/views/active_canvas/admin/partials/edit.html.erb +182 -0
  62. data/app/views/active_canvas/admin/partials/editor.html.erb +703 -0
  63. data/app/views/active_canvas/admin/partials/index.html.erb +131 -0
  64. data/app/views/active_canvas/admin/settings/show.html.erb +1864 -0
  65. data/app/views/active_canvas/pages/no_homepage.html.erb +45 -0
  66. data/app/views/active_canvas/pages/show.html.erb +113 -0
  67. data/app/views/layouts/active_canvas/admin/application.html.erb +960 -0
  68. data/app/views/layouts/active_canvas/admin/editor.html.erb +826 -0
  69. data/app/views/layouts/active_canvas/application.html.erb +55 -0
  70. data/config/routes.rb +48 -0
  71. data/db/migrate/20260202000001_create_active_canvas_tables.rb +113 -0
  72. data/db/migrate/20260202000002_create_active_canvas_ai_models.rb +26 -0
  73. data/lib/active_canvas/configuration.rb +232 -0
  74. data/lib/active_canvas/engine.rb +44 -0
  75. data/lib/active_canvas/version.rb +3 -0
  76. data/lib/active_canvas.rb +26 -0
  77. data/lib/generators/active_canvas/install/install_generator.rb +263 -0
  78. data/lib/generators/active_canvas/install/templates/initializer.rb.tt +163 -0
  79. data/lib/tasks/active_canvas_tasks.rake +69 -0
  80. metadata +150 -0
@@ -0,0 +1,826 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Edit: <%= @page&.title || @partial&.name %> - ActiveCanvas</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <meta name="csrf-token" content="<%= form_authenticity_token %>">
7
+
8
+ <!-- Font Awesome for toolbar icons -->
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
10
+
11
+ <!-- GrapeJS Core -->
12
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/grapesjs/dist/css/grapes.min.css">
13
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs"></script>
14
+
15
+ <!-- GrapeJS Plugins -->
16
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-blocks-basic"></script>
17
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-plugin-forms"></script>
18
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-preset-webpage"></script>
19
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-style-bg"></script>
20
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-tabs"></script>
21
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-custom-code"></script>
22
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-touch"></script>
23
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-parser-postcss"></script>
24
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-tooltip"></script>
25
+ <script src="https://cdn.jsdelivr.net/npm/grapesjs-typed"></script>
26
+
27
+ <!-- Monaco Editor - load after GrapeJS to avoid AMD conflicts -->
28
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/loader.min.js"></script>
29
+ <script>
30
+ require.config({ paths: { vs: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs' } });
31
+ window.MonacoEnvironment = {
32
+ getWorkerUrl: function() {
33
+ return URL.createObjectURL(new Blob([`
34
+ self.MonacoEnvironment = {
35
+ baseUrl: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min'
36
+ };
37
+ importScripts('https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.45.0/min/vs/base/worker/workerMain.js');
38
+ `], { type: 'text/javascript' }));
39
+ }
40
+ };
41
+ </script>
42
+
43
+ <!-- Inter font to match admin UI -->
44
+ <link rel="preconnect" href="https://fonts.googleapis.com">
45
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
46
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
47
+
48
+ <style>
49
+ /* Base theme variables */
50
+ :root {
51
+ /* Radii - matching admin UI */
52
+ --editor-radius: 0.5rem;
53
+ --editor-radius-lg: 0.75rem;
54
+
55
+ /* Status colors - consistent across themes */
56
+ --editor-success: #10b981;
57
+ --editor-success-light: #d1fae5;
58
+ --editor-danger: #ef4444;
59
+ --editor-danger-light: #fee2e2;
60
+ --editor-warning: #f59e0b;
61
+ --editor-warning-light: #fef3c7;
62
+ }
63
+
64
+ /* Theme: Dark (default) - Slate based */
65
+ [data-theme="dark"], :root {
66
+ --editor-primary: #6366f1;
67
+ --editor-primary-hover: #4f46e5;
68
+ --editor-primary-light: rgba(99, 102, 241, 0.15);
69
+ --editor-bg: #0f172a;
70
+ --editor-surface: #1e293b;
71
+ --editor-surface-hover: #334155;
72
+ --editor-border: #334155;
73
+ --editor-border-light: #475569;
74
+ --editor-text: #f8fafc;
75
+ --editor-text-muted: #94a3b8;
76
+ --editor-text-dim: #64748b;
77
+ --editor-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
78
+ --editor-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.4), 0 1px 2px -1px rgb(0 0 0 / 0.4);
79
+ --editor-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.4);
80
+ --editor-canvas-bg: #0f172a;
81
+ }
82
+
83
+ /* Theme: Midnight - Deep purple/blue */
84
+ [data-theme="midnight"] {
85
+ --editor-primary: #8b5cf6;
86
+ --editor-primary-hover: #7c3aed;
87
+ --editor-primary-light: rgba(139, 92, 246, 0.15);
88
+ --editor-bg: #0c0a1d;
89
+ --editor-surface: #1a1730;
90
+ --editor-surface-hover: #2d2750;
91
+ --editor-border: #2d2750;
92
+ --editor-border-light: #3d3570;
93
+ --editor-text: #f5f3ff;
94
+ --editor-text-muted: #a5a0d0;
95
+ --editor-text-dim: #7c75a8;
96
+ --editor-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.4);
97
+ --editor-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.5);
98
+ --editor-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.5);
99
+ --editor-canvas-bg: #0c0a1d;
100
+ }
101
+
102
+ /* Theme: Ocean - Teal/cyan */
103
+ [data-theme="ocean"] {
104
+ --editor-primary: #06b6d4;
105
+ --editor-primary-hover: #0891b2;
106
+ --editor-primary-light: rgba(6, 182, 212, 0.15);
107
+ --editor-bg: #0a1a1f;
108
+ --editor-surface: #0f2930;
109
+ --editor-surface-hover: #1a3d47;
110
+ --editor-border: #1a3d47;
111
+ --editor-border-light: #2a5560;
112
+ --editor-text: #ecfeff;
113
+ --editor-text-muted: #99d5df;
114
+ --editor-text-dim: #5eaab8;
115
+ --editor-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.4);
116
+ --editor-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.5);
117
+ --editor-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.5);
118
+ --editor-canvas-bg: #0a1a1f;
119
+ }
120
+
121
+ /* Theme: Light - Clean and bright */
122
+ [data-theme="light"] {
123
+ --editor-primary: #6366f1;
124
+ --editor-primary-hover: #4f46e5;
125
+ --editor-primary-light: #e0e7ff;
126
+ --editor-bg: #f1f5f9;
127
+ --editor-surface: #ffffff;
128
+ --editor-surface-hover: #f8fafc;
129
+ --editor-border: #e2e8f0;
130
+ --editor-border-light: #cbd5e1;
131
+ --editor-text: #1e293b;
132
+ --editor-text-muted: #64748b;
133
+ --editor-text-dim: #94a3b8;
134
+ --editor-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
135
+ --editor-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
136
+ --editor-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
137
+ --editor-canvas-bg: #e2e8f0;
138
+ }
139
+
140
+ /* Theme: Charcoal - Neutral dark */
141
+ [data-theme="charcoal"] {
142
+ --editor-primary: #f97316;
143
+ --editor-primary-hover: #ea580c;
144
+ --editor-primary-light: rgba(249, 115, 22, 0.15);
145
+ --editor-bg: #171717;
146
+ --editor-surface: #262626;
147
+ --editor-surface-hover: #363636;
148
+ --editor-border: #363636;
149
+ --editor-border-light: #525252;
150
+ --editor-text: #fafafa;
151
+ --editor-text-muted: #a3a3a3;
152
+ --editor-text-dim: #737373;
153
+ --editor-shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.4);
154
+ --editor-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.5), 0 1px 2px -1px rgb(0 0 0 / 0.5);
155
+ --editor-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.5), 0 2px 4px -2px rgb(0 0 0 / 0.5);
156
+ --editor-canvas-bg: #171717;
157
+ }
158
+
159
+ * { box-sizing: border-box; margin: 0; padding: 0; }
160
+
161
+ html, body {
162
+ height: 100%;
163
+ overflow: hidden;
164
+ }
165
+
166
+ body {
167
+ font-family: 'Inter', system-ui, -apple-system, sans-serif;
168
+ background: var(--editor-bg);
169
+ color: var(--editor-text);
170
+ }
171
+
172
+ /* Editor Header */
173
+ .editor-header {
174
+ height: 52px;
175
+ background: var(--editor-surface);
176
+ border-bottom: 1px solid var(--editor-border);
177
+ display: flex;
178
+ align-items: center;
179
+ justify-content: space-between;
180
+ padding: 0 1rem;
181
+ position: fixed;
182
+ top: 0;
183
+ left: 0;
184
+ right: 0;
185
+ z-index: 100;
186
+ }
187
+
188
+ .editor-header-left {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 0.75rem;
192
+ }
193
+
194
+ .editor-back {
195
+ color: var(--editor-text-muted);
196
+ text-decoration: none;
197
+ font-size: 0.8125rem;
198
+ font-weight: 500;
199
+ display: flex;
200
+ align-items: center;
201
+ gap: 0.5rem;
202
+ padding: 0.5rem 0.75rem;
203
+ border-radius: var(--editor-radius);
204
+ transition: all 0.15s ease;
205
+ }
206
+
207
+ .editor-back:hover {
208
+ color: var(--editor-text);
209
+ background: var(--editor-surface-hover);
210
+ }
211
+
212
+ .editor-title {
213
+ font-size: 0.875rem;
214
+ font-weight: 600;
215
+ color: var(--editor-text);
216
+ padding-left: 0.75rem;
217
+ letter-spacing: -0.01em;
218
+ }
219
+
220
+ .editor-header-center {
221
+ display: flex;
222
+ align-items: center;
223
+ gap: 0.25rem;
224
+ background: var(--editor-bg);
225
+ padding: 0.25rem;
226
+ border-radius: var(--editor-radius);
227
+ }
228
+
229
+ .editor-header-right {
230
+ display: flex;
231
+ align-items: center;
232
+ gap: 0.5rem;
233
+ }
234
+
235
+ /* Device Buttons */
236
+ .device-btn {
237
+ background: transparent;
238
+ border: none;
239
+ color: var(--editor-text-muted);
240
+ padding: 0.5rem 0.625rem;
241
+ border-radius: 0.375rem;
242
+ cursor: pointer;
243
+ display: flex;
244
+ align-items: center;
245
+ justify-content: center;
246
+ transition: all 0.15s ease;
247
+ }
248
+
249
+ .device-btn:hover {
250
+ color: var(--editor-text);
251
+ }
252
+
253
+ .device-btn.active {
254
+ background: var(--editor-surface);
255
+ color: var(--editor-primary);
256
+ }
257
+
258
+ .device-btn svg {
259
+ width: 18px;
260
+ height: 18px;
261
+ }
262
+
263
+ /* Header Buttons */
264
+ .header-btn {
265
+ padding: 0.5rem 0.875rem;
266
+ border-radius: var(--editor-radius);
267
+ font-size: 0.8125rem;
268
+ font-weight: 500;
269
+ cursor: pointer;
270
+ border: none;
271
+ display: flex;
272
+ align-items: center;
273
+ gap: 0.5rem;
274
+ transition: all 0.15s ease;
275
+ }
276
+
277
+ .header-btn svg {
278
+ width: 16px;
279
+ height: 16px;
280
+ }
281
+
282
+ .header-btn-secondary {
283
+ background: var(--editor-bg);
284
+ color: var(--editor-text-muted);
285
+ border: 1px solid var(--editor-border);
286
+ }
287
+
288
+ .header-btn-secondary:hover {
289
+ background: var(--editor-surface-hover);
290
+ color: var(--editor-text);
291
+ border-color: var(--editor-border-light);
292
+ }
293
+
294
+ .header-btn-primary {
295
+ background: var(--editor-primary);
296
+ color: white;
297
+ box-shadow: var(--editor-shadow-sm);
298
+ }
299
+
300
+ .header-btn-primary:hover {
301
+ background: var(--editor-primary-hover);
302
+ box-shadow: var(--editor-shadow);
303
+ }
304
+
305
+ .header-btn-primary:disabled {
306
+ opacity: 0.5;
307
+ cursor: not-allowed;
308
+ }
309
+
310
+ /* Main Editor Container */
311
+ .editor-container {
312
+ display: flex;
313
+ flex: 1;
314
+ width: 100%;
315
+ overflow: hidden;
316
+ }
317
+
318
+ /* Left Panel - Blocks */
319
+ .editor-panel-left {
320
+ width: 320px;
321
+ min-width: 320px;
322
+ background: var(--editor-surface);
323
+ border-right: 1px solid var(--editor-border);
324
+ display: flex;
325
+ flex-direction: column;
326
+ overflow: hidden;
327
+ flex-shrink: 0;
328
+ }
329
+
330
+ .panel-tabs {
331
+ display: flex;
332
+ background: var(--editor-bg);
333
+ padding: 0.5rem 0.5rem 0;
334
+ gap: 0.25rem;
335
+ }
336
+
337
+ .panel-tab {
338
+ flex: 1;
339
+ padding: 0.625rem 0.75rem;
340
+ background: transparent;
341
+ border: none;
342
+ color: var(--editor-text-muted);
343
+ font-size: 0.75rem;
344
+ font-weight: 500;
345
+ cursor: pointer;
346
+ border-radius: var(--editor-radius) var(--editor-radius) 0 0;
347
+ transition: all 0.15s ease;
348
+ }
349
+
350
+ .panel-tab:hover {
351
+ color: var(--editor-text);
352
+ background: var(--editor-surface-hover);
353
+ }
354
+
355
+ .panel-tab.active {
356
+ color: var(--editor-text);
357
+ background: var(--editor-surface);
358
+ }
359
+
360
+ .panel-content {
361
+ flex: 1;
362
+ overflow-y: auto;
363
+ padding: 0.75rem;
364
+ }
365
+
366
+ /* Canvas */
367
+ .editor-canvas {
368
+ flex: 1;
369
+ min-width: 0;
370
+ background: var(--editor-bg);
371
+ position: relative;
372
+ overflow: hidden;
373
+ }
374
+
375
+ #gjs {
376
+ height: 100%;
377
+ width: 100%;
378
+ }
379
+
380
+ /* GrapeJS Canvas - responsive device preview */
381
+ .gjs-cv-canvas {
382
+ width: 100% !important;
383
+ height: 100% !important;
384
+ display: flex;
385
+ justify-content: center;
386
+ background: var(--editor-bg);
387
+ }
388
+
389
+ .gjs-frame-wrapper {
390
+ transition: width 0.3s ease;
391
+ background: #fff;
392
+ box-shadow: 0 0 30px rgba(0, 0, 0, 0.3);
393
+ }
394
+
395
+ /* Device-specific frame styling */
396
+ .gjs-frame-wrapper[style*="width: 768px"],
397
+ .gjs-frame-wrapper[style*="width: 320px"] {
398
+ margin: 1rem auto;
399
+ border-radius: 8px;
400
+ overflow: hidden;
401
+ }
402
+
403
+ /* Right Panel - Styles/Layers */
404
+ .editor-panel-right {
405
+ width: 300px;
406
+ min-width: 300px;
407
+ background: var(--editor-surface);
408
+ border-left: 1px solid var(--editor-border);
409
+ display: flex;
410
+ flex-direction: column;
411
+ overflow: hidden;
412
+ flex-shrink: 0;
413
+ }
414
+
415
+ /* GrapeJS Overrides */
416
+ .gjs-one-bg {
417
+ background-color: var(--editor-surface);
418
+ }
419
+
420
+ .gjs-two-color {
421
+ color: var(--editor-text);
422
+ }
423
+
424
+ .gjs-three-bg {
425
+ background-color: var(--editor-bg);
426
+ }
427
+
428
+ .gjs-four-color,
429
+ .gjs-four-color-h:hover {
430
+ color: var(--editor-primary);
431
+ }
432
+
433
+ /* Blocks */
434
+ .gjs-block {
435
+ width: calc(50% - 6px);
436
+ min-height: 65px;
437
+ padding: 0.625rem;
438
+ margin: 3px;
439
+ border-radius: var(--editor-radius);
440
+ font-size: 0.6875rem;
441
+ background: var(--editor-bg);
442
+ border: 1px solid var(--editor-border);
443
+ transition: all 0.15s ease;
444
+ }
445
+
446
+ .gjs-block:hover {
447
+ border-color: var(--editor-primary);
448
+ box-shadow: 0 0 0 1px var(--editor-primary);
449
+ }
450
+
451
+ .gjs-block-label {
452
+ font-size: 0.6875rem;
453
+ font-weight: 500;
454
+ }
455
+
456
+ .gjs-block-category .gjs-title {
457
+ font-size: 0.6875rem;
458
+ font-weight: 600;
459
+ text-transform: uppercase;
460
+ letter-spacing: 0.05em;
461
+ padding: 0.875rem 0.75rem;
462
+ color: var(--editor-text-muted);
463
+ border-bottom: 1px solid var(--editor-border);
464
+ }
465
+
466
+ /* Layers */
467
+ .gjs-layers {
468
+ padding: 0.5rem;
469
+ }
470
+
471
+ .gjs-layer {
472
+ font-size: 0.75rem;
473
+ border-radius: 0.25rem;
474
+ }
475
+
476
+ .gjs-layer:hover {
477
+ background: var(--editor-surface-hover);
478
+ }
479
+
480
+ /* Style Manager */
481
+ .gjs-sm-sector {
482
+ border-bottom: 1px solid var(--editor-border);
483
+ }
484
+
485
+ .gjs-sm-sector-title {
486
+ font-size: 0.6875rem;
487
+ font-weight: 600;
488
+ text-transform: uppercase;
489
+ letter-spacing: 0.05em;
490
+ padding: 0.875rem 0.75rem;
491
+ color: var(--editor-text-muted);
492
+ }
493
+
494
+ .gjs-sm-properties {
495
+ padding: 0.75rem;
496
+ }
497
+
498
+ .gjs-sm-label {
499
+ font-size: 0.6875rem;
500
+ color: var(--editor-text-muted);
501
+ }
502
+
503
+ .gjs-field {
504
+ background: var(--editor-bg);
505
+ border: 1px solid var(--editor-border);
506
+ border-radius: 0.375rem;
507
+ }
508
+
509
+ .gjs-field:focus-within {
510
+ border-color: var(--editor-primary);
511
+ box-shadow: 0 0 0 2px var(--editor-primary-light);
512
+ }
513
+
514
+ .gjs-field input,
515
+ .gjs-field select {
516
+ font-size: 0.75rem;
517
+ color: var(--editor-text);
518
+ font-family: inherit;
519
+ }
520
+
521
+ /* Traits (Component Settings) */
522
+ .gjs-trt-traits {
523
+ padding: 0.75rem;
524
+ }
525
+
526
+ .gjs-trt-trait {
527
+ margin-bottom: 0.75rem;
528
+ }
529
+
530
+ .gjs-trt-trait .gjs-label {
531
+ font-size: 0.6875rem;
532
+ font-weight: 500;
533
+ margin-bottom: 0.375rem;
534
+ color: var(--editor-text-muted);
535
+ }
536
+
537
+ /* Selected Component */
538
+ .gjs-selected {
539
+ outline: 2px solid var(--editor-primary) !important;
540
+ outline-offset: -2px;
541
+ }
542
+
543
+ .gjs-toolbar {
544
+ background: var(--editor-primary);
545
+ border-radius: 0.375rem;
546
+ padding: 0.25rem;
547
+ }
548
+
549
+ .gjs-toolbar-item {
550
+ padding: 0.375rem 0.5rem;
551
+ font-size: 0.75rem;
552
+ border-radius: 0.25rem;
553
+ }
554
+
555
+ .gjs-toolbar-item:hover {
556
+ background: rgba(255, 255, 255, 0.2);
557
+ }
558
+
559
+ /* Rich Text Editor - styles in editor.css */
560
+
561
+ /* Canvas Frame */
562
+ .gjs-frame-wrapper {
563
+ background: #fff;
564
+ }
565
+
566
+ /* Modal */
567
+ .gjs-mdl-dialog {
568
+ background: var(--editor-surface);
569
+ border-radius: var(--editor-radius-lg);
570
+ border: 1px solid var(--editor-border);
571
+ max-width: 90vw;
572
+ width: 1000px;
573
+ box-shadow: var(--editor-shadow-md);
574
+ }
575
+
576
+ .gjs-mdl-header {
577
+ border-bottom: 1px solid var(--editor-border);
578
+ padding: 1rem 1.25rem;
579
+ }
580
+
581
+ .gjs-mdl-title {
582
+ font-size: 1rem;
583
+ font-weight: 600;
584
+ }
585
+
586
+ /* Asset Manager */
587
+ .gjs-am-assets-cont {
588
+ padding: 1rem;
589
+ }
590
+
591
+ .gjs-am-asset {
592
+ border-radius: var(--editor-radius);
593
+ margin: 0.25rem;
594
+ border: 1px solid var(--editor-border);
595
+ overflow: hidden;
596
+ }
597
+
598
+ .gjs-am-asset:hover {
599
+ border-color: var(--editor-primary);
600
+ }
601
+
602
+ .gjs-am-asset-image {
603
+ border-radius: 0;
604
+ }
605
+
606
+ .gjs-am-meta {
607
+ padding: 0.5rem;
608
+ font-size: 0.6875rem;
609
+ background: var(--editor-bg);
610
+ }
611
+
612
+ /* Code Editor Modal - styles defined in editor view for Monaco */
613
+ .gjs-mdl-content {
614
+ padding: 0;
615
+ }
616
+
617
+ /* Notification Toast */
618
+ .editor-toast {
619
+ position: fixed;
620
+ bottom: 1.5rem;
621
+ right: 1.5rem;
622
+ padding: 0.875rem 1.25rem;
623
+ border-radius: var(--editor-radius);
624
+ font-size: 0.8125rem;
625
+ font-weight: 500;
626
+ z-index: 1000;
627
+ display: none;
628
+ box-shadow: var(--editor-shadow-md);
629
+ animation: slideIn 0.3s ease;
630
+ }
631
+
632
+ .editor-toast.show {
633
+ display: flex;
634
+ align-items: center;
635
+ gap: 0.5rem;
636
+ }
637
+
638
+ .editor-toast.success {
639
+ background: var(--editor-success);
640
+ color: white;
641
+ }
642
+
643
+ .editor-toast.error {
644
+ background: var(--editor-danger);
645
+ color: white;
646
+ }
647
+
648
+ @keyframes slideIn {
649
+ from {
650
+ transform: translateY(100%);
651
+ opacity: 0;
652
+ }
653
+ to {
654
+ transform: translateY(0);
655
+ opacity: 1;
656
+ }
657
+ }
658
+
659
+ /* Loading Overlay */
660
+ .editor-loading {
661
+ position: fixed;
662
+ top: 0;
663
+ left: 0;
664
+ right: 0;
665
+ bottom: 0;
666
+ background: rgba(15, 23, 42, 0.8);
667
+ backdrop-filter: blur(4px);
668
+ display: flex;
669
+ align-items: center;
670
+ justify-content: center;
671
+ z-index: 1000;
672
+ }
673
+
674
+ .editor-loading.hidden {
675
+ display: none;
676
+ }
677
+
678
+ .editor-loading-spinner {
679
+ width: 44px;
680
+ height: 44px;
681
+ border: 3px solid var(--editor-border);
682
+ border-top-color: var(--editor-primary);
683
+ border-radius: 50%;
684
+ animation: spin 1s linear infinite;
685
+ }
686
+
687
+ @keyframes spin {
688
+ to { transform: rotate(360deg); }
689
+ }
690
+
691
+ /* Scrollbar */
692
+ ::-webkit-scrollbar {
693
+ width: 8px;
694
+ height: 8px;
695
+ }
696
+
697
+ ::-webkit-scrollbar-track {
698
+ background: var(--editor-bg);
699
+ }
700
+
701
+ ::-webkit-scrollbar-thumb {
702
+ background: var(--editor-border-light);
703
+ border-radius: 4px;
704
+ }
705
+
706
+ ::-webkit-scrollbar-thumb:hover {
707
+ background: var(--editor-text-dim);
708
+ }
709
+
710
+ /* Selection color */
711
+ ::selection {
712
+ background: var(--editor-primary);
713
+ color: white;
714
+ }
715
+
716
+ /* ==================== Theme Switcher ==================== */
717
+
718
+ .theme-switcher {
719
+ position: relative;
720
+ }
721
+
722
+ .theme-dropdown {
723
+ position: absolute;
724
+ top: calc(100% + 0.5rem);
725
+ right: 0;
726
+ background: var(--editor-surface);
727
+ border: 1px solid var(--editor-border);
728
+ border-radius: var(--editor-radius-lg);
729
+ box-shadow: var(--editor-shadow-md);
730
+ min-width: 180px;
731
+ z-index: 200;
732
+ opacity: 0;
733
+ visibility: hidden;
734
+ transform: translateY(-8px);
735
+ transition: all 0.15s ease;
736
+ }
737
+
738
+ .theme-dropdown.open {
739
+ opacity: 1;
740
+ visibility: visible;
741
+ transform: translateY(0);
742
+ }
743
+
744
+ .theme-dropdown-header {
745
+ padding: 0.75rem 1rem;
746
+ font-size: 0.6875rem;
747
+ font-weight: 600;
748
+ text-transform: uppercase;
749
+ letter-spacing: 0.05em;
750
+ color: var(--editor-text-muted);
751
+ border-bottom: 1px solid var(--editor-border);
752
+ }
753
+
754
+ .theme-option {
755
+ display: flex;
756
+ align-items: center;
757
+ gap: 0.75rem;
758
+ width: 100%;
759
+ padding: 0.625rem 1rem;
760
+ background: transparent;
761
+ border: none;
762
+ color: var(--editor-text);
763
+ font-size: 0.8125rem;
764
+ font-weight: 500;
765
+ cursor: pointer;
766
+ transition: background 0.15s ease;
767
+ text-align: left;
768
+ }
769
+
770
+ .theme-option:hover {
771
+ background: var(--editor-surface-hover);
772
+ }
773
+
774
+ .theme-option:last-child {
775
+ border-radius: 0 0 var(--editor-radius-lg) var(--editor-radius-lg);
776
+ }
777
+
778
+ .theme-preview {
779
+ width: 20px;
780
+ height: 20px;
781
+ border-radius: 50%;
782
+ border: 2px solid var(--editor-border);
783
+ flex-shrink: 0;
784
+ }
785
+
786
+ .theme-preview-dark {
787
+ background: linear-gradient(135deg, #0f172a 50%, #1e293b 50%);
788
+ }
789
+
790
+ .theme-preview-midnight {
791
+ background: linear-gradient(135deg, #0c0a1d 50%, #8b5cf6 50%);
792
+ }
793
+
794
+ .theme-preview-ocean {
795
+ background: linear-gradient(135deg, #0a1a1f 50%, #06b6d4 50%);
796
+ }
797
+
798
+ .theme-preview-charcoal {
799
+ background: linear-gradient(135deg, #171717 50%, #f97316 50%);
800
+ }
801
+
802
+ .theme-preview-light {
803
+ background: linear-gradient(135deg, #f1f5f9 50%, #6366f1 50%);
804
+ }
805
+
806
+ .theme-name {
807
+ flex: 1;
808
+ }
809
+
810
+ .theme-check {
811
+ opacity: 0;
812
+ color: var(--editor-primary);
813
+ }
814
+
815
+ .theme-option.active .theme-check {
816
+ opacity: 1;
817
+ }
818
+ </style>
819
+
820
+ <!-- ActiveCanvas Editor Overrides (loaded last to override all styles) -->
821
+ <%= stylesheet_link_tag "active_canvas/editor", media: "all" %>
822
+ </head>
823
+ <body>
824
+ <%= yield %>
825
+ </body>
826
+ </html>