vibespot 0.4.4 → 0.5.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibespot",
3
- "version": "0.4.4",
3
+ "version": "0.5.0",
4
4
  "description": "AI-powered HubSpot CMS landing page builder — vibe coding & React converter",
5
5
  "type": "module",
6
6
  "bin": {
package/ui/chat.js CHANGED
@@ -184,14 +184,14 @@ function appendUserMessage(text, timestamp) {
184
184
  const div = document.createElement("div");
185
185
  div.className = "chat-msg chat-msg--user";
186
186
  div.innerHTML = `
187
+ <div class="chat-msg__avatar chat-msg__avatar--user">Y</div>
187
188
  <div class="chat-msg__content">
188
189
  <div class="chat-msg__header">
189
190
  <span class="chat-msg__sender">You</span>
190
191
  <span class="chat-msg__time">${time}</span>
191
192
  </div>
192
193
  <div class="chat-msg__bubble">${escapeHtml(text)}</div>
193
- </div>
194
- <div class="chat-msg__avatar chat-msg__avatar--user">Y</div>`;
194
+ </div>`;
195
195
  messagesEl.appendChild(div);
196
196
  scrollToBottom();
197
197
  }
@@ -525,9 +525,11 @@ function timeAgoShort(timestamp) {
525
525
 
526
526
  function updateModuleList(moduleNames) {
527
527
  const itemsEl = document.getElementById("module-items");
528
- const countEl = document.getElementById("module-count");
528
+ const barCountEl = document.getElementById("module-count");
529
+ const slideoutCountEl = document.getElementById("slideout-module-count");
529
530
 
530
- countEl.textContent = moduleNames.length;
531
+ if (barCountEl) barCountEl.textContent = moduleNames.length;
532
+ if (slideoutCountEl) slideoutCountEl.textContent = moduleNames.length;
531
533
  itemsEl.innerHTML = "";
532
534
 
533
535
  for (const name of moduleNames) {
@@ -541,20 +543,12 @@ function updateModuleList(moduleNames) {
541
543
  <span class="module-item__delete" title="Delete module">&times;</span>
542
544
  `;
543
545
 
544
- // Click to scroll to module in preview
545
- item.querySelector(".module-item__name").addEventListener("click", () => {
546
- scrollPreviewToModule(name);
547
- highlightModuleItem(name);
548
- });
549
-
550
- // Click gear to open field editor
551
546
  item.querySelector(".module-item__edit").addEventListener("click", (e) => {
552
547
  e.stopPropagation();
553
548
  openFieldEditor(name);
554
549
  highlightModuleItem(name);
555
550
  });
556
551
 
557
- // Click × to delete module
558
552
  item.querySelector(".module-item__delete").addEventListener("click", (e) => {
559
553
  e.stopPropagation();
560
554
  confirmDeleteModule(name);
@@ -563,7 +557,6 @@ function updateModuleList(moduleNames) {
563
557
  itemsEl.appendChild(item);
564
558
  }
565
559
 
566
- // Set up drag-and-drop reordering
567
560
  setupDragReorder(itemsEl);
568
561
  }
569
562
 
@@ -573,6 +566,32 @@ function highlightModuleItem(name) {
573
566
  });
574
567
  }
575
568
 
569
+ // ---------------------------------------------------------------------------
570
+ // Module slideout
571
+ // ---------------------------------------------------------------------------
572
+
573
+ function openModuleSlideout() {
574
+ const slideout = document.getElementById("module-slideout");
575
+ document.getElementById("module-list-view").classList.remove("hidden");
576
+ document.getElementById("module-editor-view").classList.add("hidden");
577
+ slideout.classList.add("open");
578
+ }
579
+
580
+ function closeModuleSlideout() {
581
+ document.getElementById("module-slideout").classList.remove("open");
582
+ }
583
+
584
+ function showEditorView(moduleName) {
585
+ document.getElementById("module-list-view").classList.add("hidden");
586
+ document.getElementById("module-editor-view").classList.remove("hidden");
587
+ document.getElementById("module-slideout").classList.add("open");
588
+ }
589
+
590
+ function showModuleListView() {
591
+ document.getElementById("module-editor-view").classList.add("hidden");
592
+ document.getElementById("module-list-view").classList.remove("hidden");
593
+ }
594
+
576
595
  // ---------------------------------------------------------------------------
577
596
  // Module library — add modules from other templates
578
597
  // ---------------------------------------------------------------------------
@@ -646,9 +665,24 @@ async function addModuleFromLibrary(moduleName) {
646
665
  }
647
666
  }
648
667
 
649
- // Add module button listener
668
+ // Module bar button → toggle slideout
669
+ document.getElementById("btn-modules")?.addEventListener("click", () => {
670
+ const slideout = document.getElementById("module-slideout");
671
+ if (slideout.classList.contains("open")) {
672
+ closeModuleSlideout();
673
+ } else {
674
+ openModuleSlideout();
675
+ }
676
+ });
677
+
678
+ // Add module button (inside slideout)
650
679
  document.getElementById("btn-add-module").addEventListener("click", toggleModuleLibraryDropdown);
651
680
 
681
+ // Slideout close buttons
682
+ document.getElementById("module-slideout-close")?.addEventListener("click", closeModuleSlideout);
683
+ document.getElementById("field-editor-back")?.addEventListener("click", showModuleListView);
684
+ document.getElementById("field-editor-close")?.addEventListener("click", closeModuleSlideout);
685
+
652
686
  // Close dropdown when clicking outside
653
687
  document.addEventListener("click", (e) => {
654
688
  const dropdown = document.getElementById("module-library-dropdown");
@@ -658,62 +692,172 @@ document.addEventListener("click", (e) => {
658
692
  }
659
693
  });
660
694
 
661
- async function confirmDeleteModule(moduleName) {
662
- const ok = await vibeConfirm(`Delete "${escapeHtml(moduleName)}"?`, "This cannot be undone.", { confirmLabel: "Delete" });
663
- if (!ok) return;
664
-
665
- try {
666
- await fetch("/api/modules", {
667
- method: "DELETE",
668
- headers: { "Content-Type": "application/json" },
669
- body: JSON.stringify({ moduleName }),
695
+ function confirmDeleteModule(moduleName) {
696
+ return new Promise((resolve) => {
697
+ let deleteEntirely = false;
698
+
699
+ const overlay = document.createElement("div");
700
+ overlay.className = "confirm-overlay";
701
+ overlay.innerHTML = `
702
+ <div class="confirm-dialog">
703
+ <div class="confirm-dialog__title">Remove "${escapeHtml(moduleName)}"?</div>
704
+ <p class="confirm-dialog__detail">Module will be removed from this page but kept in your library.</p>
705
+ <label class="confirm-dialog__toggle">
706
+ <span class="confirm-dialog__toggle-switch">
707
+ <input type="checkbox" data-role="toggle" />
708
+ <span class="confirm-dialog__toggle-slider"></span>
709
+ </span>
710
+ <span class="confirm-dialog__toggle-label">Delete entirely</span>
711
+ </label>
712
+ <div class="confirm-dialog__toggle-warn">Cannot be undone!</div>
713
+ <div class="confirm-dialog__actions">
714
+ <button class="btn btn--secondary" data-action="cancel">Cancel</button>
715
+ <button class="btn btn--primary" data-action="confirm">Remove</button>
716
+ </div>
717
+ </div>
718
+ `;
719
+ document.body.appendChild(overlay);
720
+
721
+ const toggle = overlay.querySelector('[data-role="toggle"]');
722
+ const confirmBtn = overlay.querySelector('[data-action="confirm"]');
723
+ const warnEl = overlay.querySelector(".confirm-dialog__toggle-warn");
724
+ warnEl.style.display = "none";
725
+
726
+ toggle.addEventListener("change", () => {
727
+ deleteEntirely = toggle.checked;
728
+ if (deleteEntirely) {
729
+ confirmBtn.textContent = "Delete";
730
+ confirmBtn.className = "btn btn--danger";
731
+ warnEl.style.display = "";
732
+ } else {
733
+ confirmBtn.textContent = "Remove";
734
+ confirmBtn.className = "btn btn--primary";
735
+ warnEl.style.display = "none";
736
+ }
670
737
  });
671
- // Remove from list and refresh preview
672
- const item = document.querySelector(`.module-item[data-module="${CSS.escape(moduleName)}"]`);
673
- if (item) item.remove();
674
- const countEl = document.getElementById("module-count");
675
- countEl.textContent = document.querySelectorAll(".module-item").length;
676
- refreshPreview();
677
- } catch {
678
- // silently fail
679
- }
738
+
739
+ const close = (confirmed) => {
740
+ overlay.remove();
741
+ if (!confirmed) { resolve(); return; }
742
+
743
+ fetch("/api/modules", {
744
+ method: "DELETE",
745
+ headers: { "Content-Type": "application/json" },
746
+ body: JSON.stringify({ moduleName, deleteEntirely }),
747
+ }).then(() => {
748
+ const item = document.querySelector(`.module-item[data-module="${CSS.escape(moduleName)}"]`);
749
+ if (item) item.remove();
750
+ const countEl = document.getElementById("module-count");
751
+ const slideoutCountEl = document.getElementById("slideout-module-count");
752
+ const count = document.querySelectorAll(".module-item").length;
753
+ if (countEl) countEl.textContent = count;
754
+ if (slideoutCountEl) slideoutCountEl.textContent = count;
755
+ refreshPreview();
756
+ resolve();
757
+ }).catch(() => resolve());
758
+ };
759
+
760
+ overlay.querySelector('[data-action="cancel"]').addEventListener("click", () => close(false));
761
+ confirmBtn.addEventListener("click", () => close(true));
762
+ overlay.addEventListener("click", (e) => { if (e.target === overlay) close(false); });
763
+ });
680
764
  }
681
765
 
682
766
  // ---------------------------------------------------------------------------
683
- // Drag-and-drop reordering
767
+ // Drag-and-drop reordering (smooth sortable animation)
684
768
  // ---------------------------------------------------------------------------
685
769
 
686
770
  function setupDragReorder(container) {
687
- let dragItem = null;
688
- let dragY = 0;
689
-
690
- container.querySelectorAll(".module-item__drag").forEach((handle) => {
691
- handle.addEventListener("mousedown", (e) => {
692
- dragItem = handle.closest(".module-item");
693
- dragY = e.clientY;
694
- dragItem.style.opacity = "0.5";
695
-
696
- const onMove = (e) => {
697
- const dy = e.clientY - dragY;
698
- if (Math.abs(dy) > 30) {
699
- const items = [...container.querySelectorAll(".module-item")];
700
- const idx = items.indexOf(dragItem);
701
- if (dy > 0 && idx < items.length - 1) {
702
- container.insertBefore(items[idx + 1], dragItem);
703
- } else if (dy < 0 && idx > 0) {
704
- container.insertBefore(dragItem, items[idx - 1]);
771
+ container.querySelectorAll(".module-item").forEach((item) => {
772
+ item.addEventListener("mousedown", (e) => {
773
+ // Skip if clicking on edit/delete controls
774
+ if (e.target.closest(".module-item__edit") || e.target.closest(".module-item__delete")) return;
775
+
776
+ e.preventDefault();
777
+ const dragItem = item;
778
+ const moduleName = dragItem.dataset.module;
779
+ const startY = e.clientY;
780
+ const DRAG_THRESHOLD = 5;
781
+ let isDragging = false;
782
+ let items, startIdx, itemRects, itemHeight, currentIdx;
783
+
784
+ const startDrag = () => {
785
+ isDragging = true;
786
+ items = [...container.querySelectorAll(".module-item")];
787
+ startIdx = items.indexOf(dragItem);
788
+ itemRects = items.map((it) => it.getBoundingClientRect());
789
+ itemHeight = itemRects[startIdx]?.height || 36;
790
+ currentIdx = startIdx;
791
+
792
+ dragItem.classList.add("module-item--dragging");
793
+ items.forEach((it) => {
794
+ if (it !== dragItem) it.classList.add("module-item--placeholder");
795
+ });
796
+ };
797
+
798
+ const onMove = (ev) => {
799
+ const dy = ev.clientY - startY;
800
+
801
+ if (!isDragging) {
802
+ if (Math.abs(dy) >= DRAG_THRESHOLD) {
803
+ startDrag();
804
+ } else {
805
+ return;
705
806
  }
706
- dragY = e.clientY;
807
+ }
808
+
809
+ dragItem.style.transform = `translateY(${dy}px)`;
810
+
811
+ const cursorY = ev.clientY;
812
+ let newIdx = startIdx;
813
+ for (let i = 0; i < itemRects.length; i++) {
814
+ const mid = itemRects[i].top + itemRects[i].height / 2;
815
+ if (cursorY > mid) newIdx = i;
816
+ }
817
+ newIdx = Math.max(0, Math.min(items.length - 1, newIdx));
818
+
819
+ if (newIdx !== currentIdx) {
820
+ currentIdx = newIdx;
821
+ items.forEach((it, i) => {
822
+ if (it === dragItem) return;
823
+ if (currentIdx > startIdx && i > startIdx && i <= currentIdx) {
824
+ it.style.transform = `translateY(${-itemHeight}px)`;
825
+ } else if (currentIdx < startIdx && i >= currentIdx && i < startIdx) {
826
+ it.style.transform = `translateY(${itemHeight}px)`;
827
+ } else {
828
+ it.style.transform = "";
829
+ }
830
+ });
707
831
  }
708
832
  };
709
833
 
710
834
  const onUp = () => {
711
- if (dragItem) dragItem.style.opacity = "1";
712
- dragItem = null;
713
835
  document.removeEventListener("mousemove", onMove);
714
836
  document.removeEventListener("mouseup", onUp);
715
837
 
716
- // Send new order to server
838
+ if (!isDragging) {
839
+ // Was a click — navigate to module
840
+ scrollPreviewToModule(moduleName);
841
+ highlightModuleItem(moduleName);
842
+ return;
843
+ }
844
+
845
+ dragItem.classList.remove("module-item--dragging");
846
+ dragItem.style.transform = "";
847
+ items.forEach((it) => {
848
+ it.classList.remove("module-item--placeholder");
849
+ it.style.transform = "";
850
+ });
851
+
852
+ if (currentIdx !== startIdx) {
853
+ if (currentIdx < startIdx) {
854
+ container.insertBefore(dragItem, items[currentIdx]);
855
+ } else {
856
+ const ref = items[currentIdx].nextSibling;
857
+ container.insertBefore(dragItem, ref);
858
+ }
859
+ }
860
+
717
861
  const newOrder = [...container.querySelectorAll(".module-item")].map(
718
862
  (el) => el.dataset.module
719
863
  );
package/ui/dashboard.js CHANGED
@@ -32,6 +32,7 @@ async function showDashboard(themeName) {
32
32
  // Hide other screens
33
33
  setupScreen.classList.add("hidden");
34
34
  document.getElementById("setup-topbar").classList.add("hidden");
35
+ document.getElementById("project-rail")?.classList.remove("project-rail--expanded");
35
36
  appScreen.classList.add("hidden");
36
37
  dashboardScreen.classList.remove("hidden");
37
38
 
@@ -209,6 +210,12 @@ function renderBrandAssets(assets) {
209
210
  bvIcon.textContent = "+";
210
211
  bvIcon.classList.remove("brand-asset-upload__icon--done");
211
212
  }
213
+
214
+ // Humanify toggle
215
+ const humanifyCheckbox = document.getElementById("humanify-checkbox");
216
+ if (humanifyCheckbox) {
217
+ humanifyCheckbox.checked = assets.humanify !== false;
218
+ }
212
219
  }
213
220
 
214
221
  // ---------------------------------------------------------------------------
@@ -381,3 +388,15 @@ document.getElementById("brand-upload-styleguide").querySelector("input").addEve
381
388
  document.getElementById("brand-upload-brandvoice").querySelector("input").addEventListener("change", (e) => {
382
389
  if (e.target.files[0]) handleBrandFileSelected("brandvoice", e.target.files[0]);
383
390
  });
391
+
392
+ // Humanify toggle
393
+ const humanifyCheckbox = document.getElementById("humanify-checkbox");
394
+ if (humanifyCheckbox) {
395
+ humanifyCheckbox.addEventListener("change", async (e) => {
396
+ await fetch("/api/brand-assets", {
397
+ method: "POST",
398
+ headers: { "Content-Type": "application/json" },
399
+ body: JSON.stringify({ type: "humanify", content: e.target.checked ? "on" : "off" }),
400
+ });
401
+ });
402
+ }
@@ -2,21 +2,23 @@
2
2
  * Field editor sidebar — edit module field values with live preview.
3
3
  */
4
4
 
5
- const editorEl = document.getElementById("field-editor");
6
5
  const editorTitle = document.getElementById("field-editor-title");
7
6
  const editorContent = document.getElementById("field-editor-content");
8
- const editorClose = document.getElementById("field-editor-close");
9
7
 
10
8
  let currentEditModule = null;
11
9
 
12
10
  // ---------------------------------------------------------------------------
13
- // Open / close
11
+ // Open / close (integrated with module slideout)
14
12
  // ---------------------------------------------------------------------------
15
13
 
16
14
  async function openFieldEditor(moduleName) {
17
15
  currentEditModule = moduleName;
18
16
  editorTitle.textContent = moduleName;
19
- editorEl.classList.add("open");
17
+
18
+ // Switch slideout to editor view
19
+ if (typeof showEditorView === "function") {
20
+ showEditorView(moduleName);
21
+ }
20
22
 
21
23
  // Fetch module data
22
24
  try {
@@ -36,12 +38,12 @@ async function openFieldEditor(moduleName) {
36
38
  }
37
39
 
38
40
  function closeFieldEditor() {
39
- editorEl.classList.remove("open");
40
41
  currentEditModule = null;
42
+ if (typeof showModuleListView === "function") {
43
+ showModuleListView();
44
+ }
41
45
  }
42
46
 
43
- editorClose.addEventListener("click", closeFieldEditor);
44
-
45
47
  // ---------------------------------------------------------------------------
46
48
  // Render field form
47
49
  // ---------------------------------------------------------------------------
package/ui/index.html CHANGED
@@ -11,6 +11,29 @@
11
11
  </head>
12
12
  <body>
13
13
 
14
+ <!-- ============================================================ -->
15
+ <!-- PROJECT RAIL — persistent thin sidebar (Discord-style) -->
16
+ <!-- ============================================================ -->
17
+ <aside class="project-rail project-rail--expanded" id="project-rail">
18
+ <div class="project-rail__header">
19
+ <span class="project-rail__title">Projects</span>
20
+ <span class="project-rail__count" id="rail-project-count">0</span>
21
+ </div>
22
+ <div class="project-rail__items" id="project-rail-items"></div>
23
+ <button class="project-rail__add" id="project-rail-add" title="New project">+</button>
24
+ <div class="project-rail__footer">
25
+ <button class="project-rail__settings-btn" id="btn-setup-settings">
26
+ <svg width="14" height="14" viewBox="0 0 18 18" fill="none"><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 0 1 1.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 0 1 0 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 0 1-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 0 1-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 0 1 0-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 0 1 1.3-.7L7.5 1.5Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><circle cx="9" cy="9" r="2" stroke="currentColor" stroke-width="1.5"/></svg>
27
+ <span class="project-rail__settings-label">Settings</span>
28
+ </button>
29
+ </div>
30
+ </aside>
31
+
32
+ <!-- Tooltip element (positioned via JS) -->
33
+ <div class="project-rail__tooltip" id="project-rail-tooltip"></div>
34
+
35
+ <div class="app-body" id="app-body">
36
+
14
37
  <!-- ============================================================ -->
15
38
  <!-- SETUP SCREEN — shown until a session is created -->
16
39
  <!-- ============================================================ -->
@@ -21,24 +44,6 @@
21
44
  </div>
22
45
  </div>
23
46
  <div class="setup" id="setup-screen">
24
- <!-- Left sidebar: project list -->
25
- <aside class="setup-sidebar" id="setup-sidebar">
26
- <div class="setup-sidebar__header">
27
- <span class="setup-sidebar__title">Projects</span>
28
- <span class="setup-sidebar__count" id="sidebar-project-count">0</span>
29
- </div>
30
- <div class="setup-sidebar__list" id="sidebar-project-list">
31
- <!-- Populated by setup.js -->
32
- </div>
33
- <div class="setup-sidebar__footer">
34
- <button class="setup-sidebar__settings-btn" id="btn-setup-settings">
35
- <svg width="14" height="14" viewBox="0 0 18 18" fill="none"><path d="M7.5 1.5h3l.4 2.1a5.5 5.5 0 0 1 1.3.7l2-.8 1.5 2.6-1.6 1.3a5.5 5.5 0 0 1 0 1.5l1.6 1.3-1.5 2.6-2-.8a5.5 5.5 0 0 1-1.3.7l-.4 2.1h-3l-.4-2.1a5.5 5.5 0 0 1-1.3-.7l-2 .8-1.5-2.6 1.6-1.3a5.5 5.5 0 0 1 0-1.5L2.3 6.1l1.5-2.6 2 .8a5.5 5.5 0 0 1 1.3-.7L7.5 1.5Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><circle cx="9" cy="9" r="2" stroke="currentColor" stroke-width="1.5"/></svg>
36
- Settings
37
- </button>
38
- </div>
39
- </aside>
40
-
41
- <!-- Right side: main content -->
42
47
  <div class="setup__main">
43
48
  <div class="setup__container">
44
49
  <div class="setup__symbol">&#10022;</div>
@@ -153,6 +158,14 @@
153
158
  <span class="dashboard__section-hint">Optional — injected into AI prompts</span>
154
159
  </div>
155
160
  <div class="dashboard__brand-assets" id="dashboard-brand-assets">
161
+ <div class="brand-asset-toggle" id="brand-toggle-humanify">
162
+ <label class="brand-asset-toggle__switch">
163
+ <input type="checkbox" id="humanify-checkbox" checked>
164
+ <span class="brand-asset-toggle__slider"></span>
165
+ </label>
166
+ <span class="brand-asset-toggle__label">Humanify</span>
167
+ <span class="brand-asset-toggle__tooltip" data-tooltip="Strips AI-sounding copy: removes em dashes, banned words like 'delve' and 'leverage', cliché openers, and forced enthusiasm. Makes your landing page read like a human wrote it.">?</span>
168
+ </div>
156
169
  <label class="brand-asset-upload" id="brand-upload-styleguide">
157
170
  <input type="file" accept=".md,.txt" hidden>
158
171
  <span class="brand-asset-upload__icon" id="brand-icon-styleguide">+</span>
@@ -277,14 +290,33 @@
277
290
 
278
291
  <div class="layout">
279
292
  <aside class="panel panel--left" id="panel-left">
280
- <div class="module-list" id="module-list">
281
- <div class="module-list__header">
293
+ <div class="module-bar" id="module-bar">
294
+ <button class="module-bar__btn" id="btn-modules">
295
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
282
296
  <span>Modules</span>
283
- <span class="module-list__count" id="module-count">0</span>
284
- <button class="module-list__add-btn" id="btn-add-module" title="Add module from library">+</button>
297
+ <span class="module-bar__count" id="module-count">0</span>
298
+ </button>
299
+ </div>
300
+
301
+ <div class="module-slideout" id="module-slideout">
302
+ <div class="module-slideout__view" id="module-list-view">
303
+ <div class="module-slideout__header">
304
+ <span class="module-slideout__title">Modules</span>
305
+ <span class="module-slideout__count" id="slideout-module-count">0</span>
306
+ <button class="module-slideout__add" id="btn-add-module" title="Add module from library">+</button>
307
+ <button class="module-slideout__close" id="module-slideout-close">&times;</button>
308
+ </div>
309
+ <div class="module-library-dropdown hidden" id="module-library-dropdown"></div>
310
+ <div class="module-slideout__items" id="module-items"></div>
311
+ </div>
312
+ <div class="module-slideout__view hidden" id="module-editor-view">
313
+ <div class="module-slideout__header">
314
+ <button class="module-slideout__back" id="field-editor-back">&larr;</button>
315
+ <span class="module-slideout__title" id="field-editor-title">Fields</span>
316
+ <button class="module-slideout__close" id="field-editor-close">&times;</button>
317
+ </div>
318
+ <div class="field-editor__content" id="field-editor-content"></div>
285
319
  </div>
286
- <div class="module-list__items" id="module-items"></div>
287
- <div class="module-library-dropdown hidden" id="module-library-dropdown"></div>
288
320
  </div>
289
321
 
290
322
  <div class="chat">
@@ -337,13 +369,6 @@
337
369
  <iframe id="preview-frame" class="browser-chrome__frame" sandbox="allow-scripts allow-same-origin"></iframe>
338
370
  </div>
339
371
  </div>
340
- <aside class="field-editor" id="field-editor">
341
- <div class="field-editor__header">
342
- <h3 id="field-editor-title">Fields</h3>
343
- <button class="field-editor__close" id="field-editor-close">&times;</button>
344
- </div>
345
- <div class="field-editor__content" id="field-editor-content"></div>
346
- </aside>
347
372
  <aside class="history-panel hidden" id="history-panel">
348
373
  <div class="history-panel__header">
349
374
  <h3>Version History</h3>
@@ -392,6 +417,8 @@
392
417
  </div>
393
418
  </div>
394
419
 
420
+ </div><!-- /app-body -->
421
+
395
422
  <script src="/dialog.js"></script>
396
423
  <script src="/setup.js"></script>
397
424
  <script src="/dashboard.js"></script>