active_canvas 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4b96c0042fa7c12334c67d92ab53a6a59766099111c0abbb854b386f95c69fa
4
- data.tar.gz: 4c49e4bdcaea61447a43c4b2a4b7111904354f6f343a8a5ff17eaa08525ee0bd
3
+ metadata.gz: 379f677cf18be6230e9307e1380a024d6252a4add2b2cd954dfa6769a91b2541
4
+ data.tar.gz: fb55ab3e7801bae61423982e903cf61d35c396fb523209154a730e67b6fe93ff
5
5
  SHA512:
6
- metadata.gz: 177d5257bac30fe5da6e91cb5c65bd9584648a5173fbe7c654bbd2dd1f85f6b85d357d3a56cfc07dd3d6595a8ca7de71a8ff38f8f3ae5cc6e1215e0d7dcc50c7
7
- data.tar.gz: e02cc03e5f88c357338b92e627e707c392fa9de486157925156d619b86061db8d3079a7b4cc2278e4969f37a4a12fa324e84140fa1d737844ed5e29b1fe7918c
6
+ metadata.gz: f68e873c9f4950c4b11ecf7a673d4edf911a719bbea2f163e9da45809facebf2a5ec822293dd1077922106a507f4e017b1d1312ca7ab99a207a5212cae8fda2e
7
+ data.tar.gz: cfcb65632db1f023976acf5699880d615120f6ae669922c4f5c5d39cb0b61ce1daf0fed55a136e9b3b9d6e585d08143eca45b8446602f30b2dfa03686904eac2
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # ActiveCanvas
2
2
 
3
+ ![ActiveCanvas Demo](docs/images/active-canvas-demo.gif)
4
+
3
5
  A mountable Rails engine that turns any Rails app into a full-featured CMS. Includes a visual drag-and-drop editor (GrapeJS), AI-powered content generation, Tailwind CSS compilation, media management, page versioning, and SEO controls -- all behind an admin interface that works out of the box.
4
6
 
5
7
  ## Features
@@ -29,6 +29,7 @@
29
29
  let cssMonacoEditor = null;
30
30
  let jsMonacoEditor = null;
31
31
  let monacoInitialized = false;
32
+ let isSyncing = false;
32
33
 
33
34
  // Component editing mode
34
35
  let editingComponent = null;
@@ -201,6 +202,7 @@
201
202
  // Sync GrapeJS content to Monaco editors
202
203
  function syncGrapeJSToMonaco() {
203
204
  if (!monacoInitialized) return;
205
+ isSyncing = true;
204
206
 
205
207
  if (isComponentMode && editingComponent) {
206
208
  htmlMonacoEditor.setValue(editingComponent.toHTML());
@@ -208,7 +210,13 @@
208
210
  htmlMonacoEditor.setValue(editor.getHtml());
209
211
  }
210
212
  cssMonacoEditor.setValue(editor.getCss());
211
- setTimeout(formatAllCode, 50);
213
+ setTimeout(async () => {
214
+ try {
215
+ await formatAllCode();
216
+ } finally {
217
+ isSyncing = false;
218
+ }
219
+ }, 50);
212
220
  }
213
221
 
214
222
  // Update GrapeJS preview from Monaco
@@ -252,7 +260,9 @@
252
260
  } catch (e) {
253
261
  console.error('Error replacing component:', e);
254
262
  try {
263
+ component.components().reset();
255
264
  component.components(html);
265
+ editor.select(component);
256
266
  } catch (e2) {
257
267
  console.error('Error updating component content:', e2);
258
268
  }
@@ -285,8 +295,15 @@
285
295
  if (!monacoInitialized) {
286
296
  initMonacoEditors();
287
297
  setTimeout(() => {
298
+ isSyncing = true;
288
299
  htmlMonacoEditor.setValue(component.toHTML());
289
- setTimeout(() => htmlMonacoEditor.getAction('editor.action.formatDocument').run(), 50);
300
+ setTimeout(async () => {
301
+ try {
302
+ await htmlMonacoEditor.getAction('editor.action.formatDocument').run();
303
+ } finally {
304
+ isSyncing = false;
305
+ }
306
+ }, 50);
290
307
 
291
308
  document.getElementById('monaco-html-container').style.display = 'block';
292
309
  document.getElementById('monaco-css-container').style.display = 'none';
@@ -296,8 +313,15 @@
296
313
  htmlMonacoEditor.focus();
297
314
  }, 200);
298
315
  } else {
316
+ isSyncing = true;
299
317
  htmlMonacoEditor.setValue(component.toHTML());
300
- setTimeout(() => htmlMonacoEditor.getAction('editor.action.formatDocument').run(), 50);
318
+ setTimeout(async () => {
319
+ try {
320
+ await htmlMonacoEditor.getAction('editor.action.formatDocument').run();
321
+ } finally {
322
+ isSyncing = false;
323
+ }
324
+ }, 50);
301
325
 
302
326
  document.getElementById('monaco-html-container').style.display = 'block';
303
327
  document.getElementById('monaco-css-container').style.display = 'none';
@@ -343,6 +367,8 @@
343
367
  }
344
368
 
345
369
  function scheduleCodeUpdate() {
370
+ if (isSyncing) return;
371
+
346
372
  if (statusEl) {
347
373
  statusEl.textContent = 'Modified...';
348
374
  statusEl.className = 'code-status modified';
@@ -180,17 +180,22 @@
180
180
  document.addEventListener('keydown', function(e) {
181
181
  if ((e.ctrlKey || e.metaKey) && e.key === 's') {
182
182
  e.preventDefault();
183
+ if (document.activeElement && document.activeElement.closest('.monaco-editor')) return;
183
184
  saveContent(false);
184
185
  }
185
186
  });
186
187
 
188
+ let saveInProgress = false;
189
+
187
190
  // Auto-save every 60 seconds
188
191
  setInterval(() => {
189
192
  saveContent(true);
190
193
  }, 60000);
191
194
 
192
195
  function saveContent(isAutoSave) {
193
- const saveBtn = document.getElementById('btn-save');
196
+ if (saveInProgress) return;
197
+ saveInProgress = true;
198
+
194
199
  if (saveBtn) saveBtn.disabled = true;
195
200
 
196
201
  const html = editor.getHtml();
@@ -243,6 +248,7 @@
243
248
  console.error('Save error:', error);
244
249
  })
245
250
  .finally(() => {
251
+ saveInProgress = false;
246
252
  if (saveBtn) saveBtn.disabled = false;
247
253
  });
248
254
  }
@@ -1,3 +1,3 @@
1
1
  module ActiveCanvas
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_canvas
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Giovanni Panasiti
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2026-02-26 00:00:00.000000000 Z
10
+ date: 2026-04-30 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rails