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,572 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActiveCanvas Editor - Code Panel (Monaco Editor)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
(function() {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
window.ActiveCanvasEditor = window.ActiveCanvasEditor || {};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Setup the code panel with Monaco editors
|
|
12
|
+
* @param {Object} editor - GrapeJS editor instance
|
|
13
|
+
* @param {Object} config - Editor configuration
|
|
14
|
+
*/
|
|
15
|
+
function setupCodePanel(editor, config) {
|
|
16
|
+
const { showToast } = window.ActiveCanvasEditor;
|
|
17
|
+
|
|
18
|
+
const codePanel = document.getElementById('code-panel');
|
|
19
|
+
const btnCode = document.getElementById('btn-code');
|
|
20
|
+
const statusEl = document.getElementById('code-status');
|
|
21
|
+
const codePanelMode = document.getElementById('code-panel-mode');
|
|
22
|
+
const componentNameEl = document.getElementById('component-name');
|
|
23
|
+
|
|
24
|
+
if (!codePanel || !btnCode) return;
|
|
25
|
+
|
|
26
|
+
let currentCodeTab = 'html';
|
|
27
|
+
let codeDebounceTimer = null;
|
|
28
|
+
let htmlMonacoEditor = null;
|
|
29
|
+
let cssMonacoEditor = null;
|
|
30
|
+
let jsMonacoEditor = null;
|
|
31
|
+
let monacoInitialized = false;
|
|
32
|
+
|
|
33
|
+
// Component editing mode
|
|
34
|
+
let editingComponent = null;
|
|
35
|
+
let isComponentMode = false;
|
|
36
|
+
|
|
37
|
+
// Initialize Monaco editors
|
|
38
|
+
function initMonacoEditors() {
|
|
39
|
+
if (monacoInitialized) return;
|
|
40
|
+
|
|
41
|
+
require(['vs/editor/editor.main'], function() {
|
|
42
|
+
// Define custom themes for Monaco
|
|
43
|
+
defineMonacoThemes();
|
|
44
|
+
|
|
45
|
+
// Get Monaco theme based on editor theme
|
|
46
|
+
function getMonacoTheme() {
|
|
47
|
+
const editorTheme = document.documentElement.getAttribute('data-theme') || 'dark';
|
|
48
|
+
const themeMap = {
|
|
49
|
+
'dark': 'ac-dark',
|
|
50
|
+
'midnight': 'ac-midnight',
|
|
51
|
+
'ocean': 'ac-ocean',
|
|
52
|
+
'charcoal': 'ac-charcoal',
|
|
53
|
+
'light': 'ac-light'
|
|
54
|
+
};
|
|
55
|
+
return themeMap[editorTheme] || 'ac-dark';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Expose theme setter for external use
|
|
59
|
+
window.ActiveCanvasEditor.setMonacoTheme = function(theme) {
|
|
60
|
+
const themeMap = {
|
|
61
|
+
'dark': 'ac-dark',
|
|
62
|
+
'midnight': 'ac-midnight',
|
|
63
|
+
'ocean': 'ac-ocean',
|
|
64
|
+
'charcoal': 'ac-charcoal',
|
|
65
|
+
'light': 'ac-light'
|
|
66
|
+
};
|
|
67
|
+
const monacoTheme = themeMap[theme] || 'ac-dark';
|
|
68
|
+
monaco.editor.setTheme(monacoTheme);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Common editor options
|
|
72
|
+
const editorOptions = {
|
|
73
|
+
theme: getMonacoTheme(),
|
|
74
|
+
fontSize: 13,
|
|
75
|
+
lineNumbers: 'on',
|
|
76
|
+
minimap: { enabled: false },
|
|
77
|
+
scrollBeyondLastLine: false,
|
|
78
|
+
automaticLayout: true,
|
|
79
|
+
tabSize: 2,
|
|
80
|
+
wordWrap: 'on',
|
|
81
|
+
folding: true,
|
|
82
|
+
lineDecorationsWidth: 10,
|
|
83
|
+
lineNumbersMinChars: 3,
|
|
84
|
+
renderLineHighlight: 'line',
|
|
85
|
+
scrollbar: {
|
|
86
|
+
verticalScrollbarSize: 10,
|
|
87
|
+
horizontalScrollbarSize: 10
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Create HTML editor
|
|
92
|
+
htmlMonacoEditor = monaco.editor.create(document.getElementById('monaco-html-container'), {
|
|
93
|
+
value: editor.getHtml(),
|
|
94
|
+
language: 'html',
|
|
95
|
+
...editorOptions
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Create CSS editor
|
|
99
|
+
cssMonacoEditor = monaco.editor.create(document.getElementById('monaco-css-container'), {
|
|
100
|
+
value: editor.getCss(),
|
|
101
|
+
language: 'css',
|
|
102
|
+
...editorOptions
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Create JS editor
|
|
106
|
+
jsMonacoEditor = monaco.editor.create(document.getElementById('monaco-js-container'), {
|
|
107
|
+
value: config.contentJs || '',
|
|
108
|
+
language: 'javascript',
|
|
109
|
+
...editorOptions
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Live update on content change
|
|
113
|
+
htmlMonacoEditor.onDidChangeModelContent(scheduleCodeUpdate);
|
|
114
|
+
cssMonacoEditor.onDidChangeModelContent(scheduleCodeUpdate);
|
|
115
|
+
jsMonacoEditor.onDidChangeModelContent(scheduleJsUpdate);
|
|
116
|
+
|
|
117
|
+
// Add keyboard shortcuts to Monaco
|
|
118
|
+
addMonacoCommands(htmlMonacoEditor);
|
|
119
|
+
addMonacoCommands(cssMonacoEditor);
|
|
120
|
+
addMonacoCommands(jsMonacoEditor);
|
|
121
|
+
|
|
122
|
+
monacoInitialized = true;
|
|
123
|
+
|
|
124
|
+
// Auto-format on first open
|
|
125
|
+
setTimeout(formatAllCode, 100);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function defineMonacoThemes() {
|
|
130
|
+
monaco.editor.defineTheme('ac-dark', {
|
|
131
|
+
base: 'vs-dark',
|
|
132
|
+
inherit: true,
|
|
133
|
+
rules: [],
|
|
134
|
+
colors: {
|
|
135
|
+
'editor.background': '#1e293b',
|
|
136
|
+
'editor.foreground': '#f8fafc',
|
|
137
|
+
'editorLineNumber.foreground': '#64748b',
|
|
138
|
+
'editorCursor.foreground': '#f8fafc',
|
|
139
|
+
'editor.selectionBackground': '#334155',
|
|
140
|
+
'editor.lineHighlightBackground': '#334155'
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
monaco.editor.defineTheme('ac-midnight', {
|
|
145
|
+
base: 'vs-dark',
|
|
146
|
+
inherit: true,
|
|
147
|
+
rules: [],
|
|
148
|
+
colors: {
|
|
149
|
+
'editor.background': '#1a1730',
|
|
150
|
+
'editor.foreground': '#f5f3ff',
|
|
151
|
+
'editorLineNumber.foreground': '#7c75a8',
|
|
152
|
+
'editorCursor.foreground': '#8b5cf6',
|
|
153
|
+
'editor.selectionBackground': '#2d2750',
|
|
154
|
+
'editor.lineHighlightBackground': '#2d2750'
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
monaco.editor.defineTheme('ac-ocean', {
|
|
159
|
+
base: 'vs-dark',
|
|
160
|
+
inherit: true,
|
|
161
|
+
rules: [],
|
|
162
|
+
colors: {
|
|
163
|
+
'editor.background': '#0f2930',
|
|
164
|
+
'editor.foreground': '#ecfeff',
|
|
165
|
+
'editorLineNumber.foreground': '#5eaab8',
|
|
166
|
+
'editorCursor.foreground': '#06b6d4',
|
|
167
|
+
'editor.selectionBackground': '#1a3d47',
|
|
168
|
+
'editor.lineHighlightBackground': '#1a3d47'
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
monaco.editor.defineTheme('ac-charcoal', {
|
|
173
|
+
base: 'vs-dark',
|
|
174
|
+
inherit: true,
|
|
175
|
+
rules: [],
|
|
176
|
+
colors: {
|
|
177
|
+
'editor.background': '#262626',
|
|
178
|
+
'editor.foreground': '#fafafa',
|
|
179
|
+
'editorLineNumber.foreground': '#737373',
|
|
180
|
+
'editorCursor.foreground': '#f97316',
|
|
181
|
+
'editor.selectionBackground': '#363636',
|
|
182
|
+
'editor.lineHighlightBackground': '#363636'
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
monaco.editor.defineTheme('ac-light', {
|
|
187
|
+
base: 'vs',
|
|
188
|
+
inherit: true,
|
|
189
|
+
rules: [],
|
|
190
|
+
colors: {
|
|
191
|
+
'editor.background': '#ffffff',
|
|
192
|
+
'editor.foreground': '#1e293b',
|
|
193
|
+
'editorLineNumber.foreground': '#94a3b8',
|
|
194
|
+
'editorCursor.foreground': '#6366f1',
|
|
195
|
+
'editor.selectionBackground': '#e0e7ff',
|
|
196
|
+
'editor.lineHighlightBackground': '#f8fafc'
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Sync GrapeJS content to Monaco editors
|
|
202
|
+
function syncGrapeJSToMonaco() {
|
|
203
|
+
if (!monacoInitialized) return;
|
|
204
|
+
|
|
205
|
+
if (isComponentMode && editingComponent) {
|
|
206
|
+
htmlMonacoEditor.setValue(editingComponent.toHTML());
|
|
207
|
+
} else {
|
|
208
|
+
htmlMonacoEditor.setValue(editor.getHtml());
|
|
209
|
+
}
|
|
210
|
+
cssMonacoEditor.setValue(editor.getCss());
|
|
211
|
+
setTimeout(formatAllCode, 50);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Update GrapeJS preview from Monaco
|
|
215
|
+
function updatePreviewFromCode() {
|
|
216
|
+
if (!monacoInitialized) return;
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
if (isComponentMode && editingComponent) {
|
|
220
|
+
const newHtml = htmlMonacoEditor.getValue();
|
|
221
|
+
updateComponentFromHtml(editingComponent, newHtml);
|
|
222
|
+
} else {
|
|
223
|
+
const newHtml = htmlMonacoEditor.getValue();
|
|
224
|
+
const newCss = cssMonacoEditor.getValue();
|
|
225
|
+
editor.setComponents(newHtml);
|
|
226
|
+
editor.setStyle(newCss);
|
|
227
|
+
}
|
|
228
|
+
if (statusEl) {
|
|
229
|
+
statusEl.textContent = 'Synced';
|
|
230
|
+
statusEl.className = 'code-status synced';
|
|
231
|
+
}
|
|
232
|
+
} catch (e) {
|
|
233
|
+
if (statusEl) {
|
|
234
|
+
statusEl.textContent = 'Error';
|
|
235
|
+
statusEl.className = 'code-status modified';
|
|
236
|
+
}
|
|
237
|
+
console.error('Code parse error:', e);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Update a component from HTML string
|
|
242
|
+
function updateComponentFromHtml(component, html) {
|
|
243
|
+
if (!component) return;
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const newComponents = component.replaceWith(html);
|
|
247
|
+
|
|
248
|
+
if (newComponents && newComponents.length > 0) {
|
|
249
|
+
editingComponent = newComponents[0];
|
|
250
|
+
editor.select(editingComponent);
|
|
251
|
+
}
|
|
252
|
+
} catch (e) {
|
|
253
|
+
console.error('Error replacing component:', e);
|
|
254
|
+
try {
|
|
255
|
+
component.components(html);
|
|
256
|
+
} catch (e2) {
|
|
257
|
+
console.error('Error updating component content:', e2);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Open code panel in component editing mode
|
|
263
|
+
function openComponentCodeEditor(component) {
|
|
264
|
+
editingComponent = component;
|
|
265
|
+
isComponentMode = true;
|
|
266
|
+
|
|
267
|
+
if (codePanelMode) codePanelMode.style.display = 'flex';
|
|
268
|
+
if (componentNameEl) componentNameEl.textContent = getComponentName(component);
|
|
269
|
+
|
|
270
|
+
// Hide CSS/JS tabs in component mode
|
|
271
|
+
document.querySelectorAll('.code-panel-tab').forEach(t => {
|
|
272
|
+
if (t.dataset.type === 'html') {
|
|
273
|
+
t.style.display = '';
|
|
274
|
+
t.classList.add('active');
|
|
275
|
+
} else {
|
|
276
|
+
t.style.display = 'none';
|
|
277
|
+
t.classList.remove('active');
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
currentCodeTab = 'html';
|
|
281
|
+
|
|
282
|
+
codePanel.classList.add('open');
|
|
283
|
+
btnCode.classList.add('active');
|
|
284
|
+
|
|
285
|
+
if (!monacoInitialized) {
|
|
286
|
+
initMonacoEditors();
|
|
287
|
+
setTimeout(() => {
|
|
288
|
+
htmlMonacoEditor.setValue(component.toHTML());
|
|
289
|
+
setTimeout(() => htmlMonacoEditor.getAction('editor.action.formatDocument').run(), 50);
|
|
290
|
+
|
|
291
|
+
document.getElementById('monaco-html-container').style.display = 'block';
|
|
292
|
+
document.getElementById('monaco-css-container').style.display = 'none';
|
|
293
|
+
document.getElementById('monaco-js-container').style.display = 'none';
|
|
294
|
+
|
|
295
|
+
htmlMonacoEditor.layout();
|
|
296
|
+
htmlMonacoEditor.focus();
|
|
297
|
+
}, 200);
|
|
298
|
+
} else {
|
|
299
|
+
htmlMonacoEditor.setValue(component.toHTML());
|
|
300
|
+
setTimeout(() => htmlMonacoEditor.getAction('editor.action.formatDocument').run(), 50);
|
|
301
|
+
|
|
302
|
+
document.getElementById('monaco-html-container').style.display = 'block';
|
|
303
|
+
document.getElementById('monaco-css-container').style.display = 'none';
|
|
304
|
+
document.getElementById('monaco-js-container').style.display = 'none';
|
|
305
|
+
|
|
306
|
+
htmlMonacoEditor.layout();
|
|
307
|
+
htmlMonacoEditor.focus();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
setTimeout(() => editor.refresh(), 50);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Expose for external use
|
|
314
|
+
window.ActiveCanvasEditor.openComponentCodeEditor = openComponentCodeEditor;
|
|
315
|
+
|
|
316
|
+
// Get a friendly name for a component
|
|
317
|
+
function getComponentName(component) {
|
|
318
|
+
const type = component.get('type');
|
|
319
|
+
const tagName = component.get('tagName') || 'div';
|
|
320
|
+
const classes = component.getClasses().slice(0, 2).join('.');
|
|
321
|
+
const id = component.getId();
|
|
322
|
+
|
|
323
|
+
if (id) return `#${id}`;
|
|
324
|
+
if (classes) return `${tagName}.${classes}`;
|
|
325
|
+
if (type && type !== 'default') return type;
|
|
326
|
+
return tagName;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Exit component mode
|
|
330
|
+
function exitComponentMode() {
|
|
331
|
+
const wasInComponentMode = isComponentMode;
|
|
332
|
+
isComponentMode = false;
|
|
333
|
+
editingComponent = null;
|
|
334
|
+
if (codePanelMode) codePanelMode.style.display = 'none';
|
|
335
|
+
|
|
336
|
+
document.querySelectorAll('.code-panel-tab').forEach(t => {
|
|
337
|
+
t.style.display = '';
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
if (wasInComponentMode && monacoInitialized) {
|
|
341
|
+
syncGrapeJSToMonaco();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function scheduleCodeUpdate() {
|
|
346
|
+
if (statusEl) {
|
|
347
|
+
statusEl.textContent = 'Modified...';
|
|
348
|
+
statusEl.className = 'code-status modified';
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (codeDebounceTimer) {
|
|
352
|
+
clearTimeout(codeDebounceTimer);
|
|
353
|
+
}
|
|
354
|
+
codeDebounceTimer = setTimeout(updatePreviewFromCode, 200);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function formatAllCode() {
|
|
358
|
+
if (!monacoInitialized) return;
|
|
359
|
+
await htmlMonacoEditor.getAction('editor.action.formatDocument').run();
|
|
360
|
+
await cssMonacoEditor.getAction('editor.action.formatDocument').run();
|
|
361
|
+
await jsMonacoEditor.getAction('editor.action.formatDocument').run();
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function scheduleJsUpdate() {
|
|
365
|
+
if (statusEl) {
|
|
366
|
+
statusEl.textContent = 'Modified...';
|
|
367
|
+
statusEl.className = 'code-status modified';
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (codeDebounceTimer) {
|
|
371
|
+
clearTimeout(codeDebounceTimer);
|
|
372
|
+
}
|
|
373
|
+
codeDebounceTimer = setTimeout(() => {
|
|
374
|
+
injectJsIntoCanvas();
|
|
375
|
+
if (statusEl) {
|
|
376
|
+
statusEl.textContent = 'Synced';
|
|
377
|
+
statusEl.className = 'code-status synced';
|
|
378
|
+
}
|
|
379
|
+
}, 200);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function injectJsIntoCanvas() {
|
|
383
|
+
if (!monacoInitialized) return;
|
|
384
|
+
|
|
385
|
+
const jsCode = jsMonacoEditor.getValue();
|
|
386
|
+
const frame = editor.Canvas.getFrameEl();
|
|
387
|
+
if (!frame || !frame.contentDocument) return;
|
|
388
|
+
|
|
389
|
+
const existingScript = frame.contentDocument.getElementById('active-canvas-custom-js');
|
|
390
|
+
if (existingScript) {
|
|
391
|
+
existingScript.remove();
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (jsCode.trim()) {
|
|
395
|
+
const script = frame.contentDocument.createElement('script');
|
|
396
|
+
script.id = 'active-canvas-custom-js';
|
|
397
|
+
script.textContent = jsCode;
|
|
398
|
+
frame.contentDocument.body.appendChild(script);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function addMonacoCommands(monacoEditor) {
|
|
403
|
+
monacoEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, async () => {
|
|
404
|
+
if (codeDebounceTimer) {
|
|
405
|
+
clearTimeout(codeDebounceTimer);
|
|
406
|
+
}
|
|
407
|
+
await formatAllCode();
|
|
408
|
+
updatePreviewFromCode();
|
|
409
|
+
showToast('Formatted and applied', 'success');
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
monacoEditor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KeyF, () => {
|
|
413
|
+
monacoEditor.getAction('editor.action.formatDocument').run();
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function toggleCodePanel() {
|
|
418
|
+
const isOpen = codePanel.classList.toggle('open');
|
|
419
|
+
btnCode.classList.toggle('active', isOpen);
|
|
420
|
+
|
|
421
|
+
if (isOpen) {
|
|
422
|
+
exitComponentMode();
|
|
423
|
+
|
|
424
|
+
if (!monacoInitialized) {
|
|
425
|
+
initMonacoEditors();
|
|
426
|
+
} else {
|
|
427
|
+
syncGrapeJSToMonaco();
|
|
428
|
+
}
|
|
429
|
+
setTimeout(() => {
|
|
430
|
+
if (htmlMonacoEditor) htmlMonacoEditor.layout();
|
|
431
|
+
if (cssMonacoEditor) cssMonacoEditor.layout();
|
|
432
|
+
if (jsMonacoEditor) jsMonacoEditor.layout();
|
|
433
|
+
editor.refresh();
|
|
434
|
+
}, 50);
|
|
435
|
+
} else {
|
|
436
|
+
exitComponentMode();
|
|
437
|
+
editor.refresh();
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Event listeners
|
|
442
|
+
btnCode.addEventListener('click', toggleCodePanel);
|
|
443
|
+
|
|
444
|
+
// Keyboard shortcut: Ctrl+E / Cmd+E to toggle code editor
|
|
445
|
+
document.addEventListener('keydown', function(e) {
|
|
446
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'e') {
|
|
447
|
+
e.preventDefault();
|
|
448
|
+
toggleCodePanel();
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
const closeBtn = document.getElementById('code-close');
|
|
453
|
+
if (closeBtn) {
|
|
454
|
+
closeBtn.addEventListener('click', () => {
|
|
455
|
+
exitComponentMode();
|
|
456
|
+
codePanel.classList.remove('open');
|
|
457
|
+
btnCode.classList.remove('active');
|
|
458
|
+
editor.refresh();
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const fullPageBtn = document.getElementById('code-full-page');
|
|
463
|
+
if (fullPageBtn) {
|
|
464
|
+
fullPageBtn.addEventListener('click', () => {
|
|
465
|
+
exitComponentMode();
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
document.querySelectorAll('.code-panel-tab').forEach(tab => {
|
|
470
|
+
tab.addEventListener('click', function() {
|
|
471
|
+
currentCodeTab = this.dataset.type;
|
|
472
|
+
document.querySelectorAll('.code-panel-tab').forEach(t => t.classList.remove('active'));
|
|
473
|
+
this.classList.add('active');
|
|
474
|
+
|
|
475
|
+
const htmlContainer = document.getElementById('monaco-html-container');
|
|
476
|
+
const cssContainer = document.getElementById('monaco-css-container');
|
|
477
|
+
const jsContainer = document.getElementById('monaco-js-container');
|
|
478
|
+
|
|
479
|
+
if (htmlContainer) htmlContainer.style.display = 'none';
|
|
480
|
+
if (cssContainer) cssContainer.style.display = 'none';
|
|
481
|
+
if (jsContainer) jsContainer.style.display = 'none';
|
|
482
|
+
|
|
483
|
+
if (currentCodeTab === 'html' && htmlContainer) {
|
|
484
|
+
htmlContainer.style.display = 'block';
|
|
485
|
+
if (htmlMonacoEditor) {
|
|
486
|
+
htmlMonacoEditor.layout();
|
|
487
|
+
htmlMonacoEditor.focus();
|
|
488
|
+
}
|
|
489
|
+
} else if (currentCodeTab === 'css' && cssContainer) {
|
|
490
|
+
cssContainer.style.display = 'block';
|
|
491
|
+
if (cssMonacoEditor) {
|
|
492
|
+
cssMonacoEditor.layout();
|
|
493
|
+
cssMonacoEditor.focus();
|
|
494
|
+
}
|
|
495
|
+
} else if (currentCodeTab === 'js' && jsContainer) {
|
|
496
|
+
jsContainer.style.display = 'block';
|
|
497
|
+
if (jsMonacoEditor) {
|
|
498
|
+
jsMonacoEditor.layout();
|
|
499
|
+
jsMonacoEditor.focus();
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
});
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
const applyBtn = document.getElementById('code-apply');
|
|
506
|
+
if (applyBtn) {
|
|
507
|
+
applyBtn.addEventListener('click', async () => {
|
|
508
|
+
if (codeDebounceTimer) {
|
|
509
|
+
clearTimeout(codeDebounceTimer);
|
|
510
|
+
}
|
|
511
|
+
await formatAllCode();
|
|
512
|
+
updatePreviewFromCode();
|
|
513
|
+
showToast('Formatted and applied', 'success');
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const formatBtn = document.getElementById('code-format');
|
|
518
|
+
if (formatBtn) {
|
|
519
|
+
formatBtn.addEventListener('click', async () => {
|
|
520
|
+
await formatAllCode();
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Resize functionality
|
|
525
|
+
setupResizeHandle();
|
|
526
|
+
|
|
527
|
+
function setupResizeHandle() {
|
|
528
|
+
const resizeHandle = document.getElementById('code-panel-resize');
|
|
529
|
+
if (!resizeHandle) return;
|
|
530
|
+
|
|
531
|
+
let isResizing = false;
|
|
532
|
+
let startY = 0;
|
|
533
|
+
let startHeight = 0;
|
|
534
|
+
|
|
535
|
+
resizeHandle.addEventListener('mousedown', (e) => {
|
|
536
|
+
isResizing = true;
|
|
537
|
+
startY = e.clientY;
|
|
538
|
+
startHeight = codePanel.offsetHeight;
|
|
539
|
+
document.body.style.cursor = 'ns-resize';
|
|
540
|
+
document.body.style.userSelect = 'none';
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
document.addEventListener('mousemove', (e) => {
|
|
544
|
+
if (!isResizing) return;
|
|
545
|
+
const deltaY = startY - e.clientY;
|
|
546
|
+
const newHeight = Math.min(Math.max(startHeight + deltaY, 150), window.innerHeight * 0.7);
|
|
547
|
+
codePanel.style.height = newHeight + 'px';
|
|
548
|
+
if (htmlMonacoEditor) htmlMonacoEditor.layout();
|
|
549
|
+
if (cssMonacoEditor) cssMonacoEditor.layout();
|
|
550
|
+
if (jsMonacoEditor) jsMonacoEditor.layout();
|
|
551
|
+
editor.refresh();
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
document.addEventListener('mouseup', () => {
|
|
555
|
+
if (isResizing) {
|
|
556
|
+
isResizing = false;
|
|
557
|
+
document.body.style.cursor = '';
|
|
558
|
+
document.body.style.userSelect = '';
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// Expose getters for save function
|
|
564
|
+
window.ActiveCanvasEditor.getJs = function() {
|
|
565
|
+
return jsMonacoEditor ? jsMonacoEditor.getValue() : (config.contentJs || '');
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Expose function
|
|
570
|
+
window.ActiveCanvasEditor.setupCodePanel = setupCodePanel;
|
|
571
|
+
|
|
572
|
+
})();
|