vibespot 1.2.0 → 1.3.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.
Files changed (43) hide show
  1. package/README.md +44 -5
  2. package/assets/blog-rules.md +251 -0
  3. package/assets/email-rules.md +390 -0
  4. package/assets/humanify-guide.md +300 -101
  5. package/assets/plan-templates/blog-content-hub.md +18 -9
  6. package/assets/plan-templates/email-announcement.md +41 -0
  7. package/assets/plan-templates/email-event-invite.md +43 -0
  8. package/assets/plan-templates/email-newsletter.md +41 -0
  9. package/assets/plan-templates/email-re-engagement.md +42 -0
  10. package/assets/plan-templates/email-welcome.md +41 -0
  11. package/dist/index.js +1460 -387
  12. package/dist/index.js.map +1 -1
  13. package/package.json +5 -5
  14. package/starters/06-blog-content-hub.json +75 -0
  15. package/starters/06-email-welcome.json +60 -0
  16. package/starters/07-email-announcement.json +60 -0
  17. package/starters/08-email-newsletter.json +52 -0
  18. package/ui/chat.js +777 -63
  19. package/ui/code-editor.js +49 -7
  20. package/ui/dashboard.js +379 -93
  21. package/ui/docs/docs.css +29 -0
  22. package/ui/docs/index.html +186 -108
  23. package/ui/docs/screenshots/brand-kit-preview.png +0 -0
  24. package/ui/docs/screenshots/content-type-dropdown.png +0 -0
  25. package/ui/docs/screenshots/editor-full-layout.png +0 -0
  26. package/ui/docs/screenshots/inline-wysiwyg-editing.png +0 -0
  27. package/ui/docs/screenshots/multi-page-tree.png +0 -0
  28. package/ui/docs/screenshots/onboarding-walkthrough.png +0 -0
  29. package/ui/docs/screenshots/split-pane-view.png +0 -0
  30. package/ui/docs/screenshots/visual-controls-toolbar.png +0 -0
  31. package/ui/docs/screenshots/workspace-tabs.png +0 -0
  32. package/ui/email-preview.js +109 -0
  33. package/ui/field-editor.js +72 -1
  34. package/ui/icons.js +120 -0
  35. package/ui/index.html +877 -629
  36. package/ui/inline-edit.js +710 -0
  37. package/ui/plan.js +0 -0
  38. package/ui/preview.js +101 -198
  39. package/ui/section-controls.js +628 -0
  40. package/ui/settings.js +58 -16
  41. package/ui/setup.js +750 -140
  42. package/ui/styles.css +3430 -952
  43. package/ui/upload-panel.js +47 -20
package/ui/settings.js CHANGED
@@ -24,6 +24,8 @@ const ENGINE_LABELS = {
24
24
  // Open / Close
25
25
  // ---------------------------------------------------------------------------
26
26
 
27
+ let _prevWorkspaceTab = "pages";
28
+
27
29
  function openSettings(tab) {
28
30
  if (typeof closeMenu === "function") closeMenu();
29
31
  if (tab) {
@@ -31,12 +33,24 @@ function openSettings(tab) {
31
33
  const tabs = document.querySelectorAll("#settings-tabs .settings__tab");
32
34
  tabs.forEach((t) => t.classList.toggle("active", t.dataset.tab === tab));
33
35
  }
34
- document.getElementById("settings-overlay").classList.remove("hidden");
36
+ const overlay = document.getElementById("settings-overlay");
37
+ if (overlay) {
38
+ overlay.classList.remove("hidden");
39
+ } else if (typeof switchWorkspaceTab === "function") {
40
+ const activeWs = document.querySelector(".workspace-tab.active");
41
+ if (activeWs && activeWs.dataset.wsTab !== "settings") _prevWorkspaceTab = activeWs.dataset.wsTab;
42
+ switchWorkspaceTab("settings");
43
+ }
35
44
  refreshSettings();
36
45
  }
37
46
 
38
47
  function closeSettings() {
39
- document.getElementById("settings-overlay").classList.add("hidden");
48
+ const overlay = document.getElementById("settings-overlay");
49
+ if (overlay) {
50
+ overlay.classList.add("hidden");
51
+ } else if (typeof switchWorkspaceTab === "function") {
52
+ switchWorkspaceTab(_prevWorkspaceTab || "pages");
53
+ }
40
54
  Object.keys(activePolls).forEach((id) => {
41
55
  clearInterval(activePolls[id]);
42
56
  delete activePolls[id];
@@ -67,15 +81,43 @@ async function refreshSettings() {
67
81
  const body = document.getElementById("settings-body");
68
82
  body.innerHTML = `<div class="settings__loading"><div class="settings__spinner-lg"></div><span>Loading environment...</span></div>`;
69
83
 
84
+ const controller = new AbortController();
85
+ const timeout = setTimeout(() => controller.abort(), 3000);
86
+
70
87
  try {
71
- const res = await fetch("/api/settings/status");
88
+ const res = await fetch("/api/settings/status", { signal: controller.signal });
89
+ clearTimeout(timeout);
72
90
  settingsData = await res.json();
73
91
  renderSettings(settingsData);
74
92
  } catch (err) {
75
- body.innerHTML = `<div class="settings__loading" style="color:var(--error)">Failed to load settings</div>`;
93
+ clearTimeout(timeout);
94
+ const aborted = err && err.name === "AbortError";
95
+ renderSettingsError(body, aborted);
76
96
  }
77
97
  }
78
98
 
99
+ function renderSettingsError(body, timedOut) {
100
+ const message = timedOut
101
+ ? "Settings took too long to load. The server may be busy or unreachable."
102
+ : "Failed to load settings.";
103
+ body.innerHTML = "";
104
+ const wrap = el("div", "settings__empty-state");
105
+ wrap.style.cssText = "padding:32px;text-align:center;color:var(--text-muted);";
106
+ const heading = el("div", "");
107
+ heading.style.cssText = "font-weight:600;color:var(--text);margin-bottom:8px;";
108
+ heading.textContent = timedOut ? "Settings unavailable" : "Couldn't load settings";
109
+ wrap.appendChild(heading);
110
+ const desc = el("p", "");
111
+ desc.style.cssText = "margin:0 0 16px;font-size:13px;";
112
+ desc.textContent = message;
113
+ wrap.appendChild(desc);
114
+ const retry = el("button", "btn btn--sm btn--primary");
115
+ retry.textContent = "Retry";
116
+ retry.addEventListener("click", () => refreshSettings());
117
+ wrap.appendChild(retry);
118
+ body.appendChild(wrap);
119
+ }
120
+
79
121
  function renderSettings(data) {
80
122
  const body = document.getElementById("settings-body");
81
123
  body.innerHTML = "";
@@ -100,7 +142,7 @@ function renderAITab(body, data) {
100
142
  // Engine selector
101
143
  const section = el("section", "settings__section");
102
144
  section.appendChild(sectionTitle("Engine"));
103
- section.appendChild(desc("Choose which AI engine generates your HubSpot sections. API engines need an API key. CLI engines need the tool installed on your system."));
145
+ section.appendChild(desc("Choose which AI engine generates your HubSpot modules. API engines need an API key. CLI engines need the tool installed on your system."));
104
146
 
105
147
  const selectEl = el("div", "settings__engine-select");
106
148
  const allEngines = [
@@ -176,9 +218,9 @@ function renderAITab(body, data) {
176
218
  const agenticMode = config.agenticMode;
177
219
 
178
220
  if (isCli) {
179
- agenticSection.appendChild(desc("Decompose AI generation into specialized agents with per-section parallel calls. Better quality and structured output enforcement. CLI engines use subprocess calls — may be slower than API engines."));
221
+ agenticSection.appendChild(desc("Decompose AI generation into specialized agents with per-module parallel calls. Better quality and structured output enforcement. CLI engines use subprocess calls — may be slower than API engines."));
180
222
  } else {
181
- agenticSection.appendChild(desc("Decompose AI generation into specialized agents with per-section parallel calls. Better quality and structured output enforcement."));
223
+ agenticSection.appendChild(desc("Decompose AI generation into specialized agents with per-module parallel calls. Better quality and structured output enforcement."));
182
224
  }
183
225
 
184
226
  const toggleRow = el("div", "settings__toggle-row");
@@ -189,7 +231,7 @@ function renderAITab(body, data) {
189
231
 
190
232
  const sub = el("div", "settings__toggle-label-sub");
191
233
  if (agenticMode === true) {
192
- sub.textContent = "Active — multi-stage pipeline with parallel section generation";
234
+ sub.textContent = "Active — multi-stage pipeline with parallel module generation";
193
235
  sub.style.color = "var(--success)";
194
236
  } else if (agenticMode === false) {
195
237
  sub.textContent = "Disabled — using single-call mode";
@@ -1057,12 +1099,16 @@ async function switchHsAccount(portalId, btn) {
1057
1099
  });
1058
1100
  const data = await res.json();
1059
1101
  if (data.jobId) {
1060
- pollJob(data.jobId, () => refreshSettings(), () => {
1102
+ pollJob(data.jobId, () => {
1103
+ refreshSettings();
1104
+ if (typeof refreshPortalIndicator === "function") refreshPortalIndicator();
1105
+ }, () => {
1061
1106
  btn.textContent = "Failed";
1062
1107
  btn.disabled = false;
1063
1108
  });
1064
1109
  } else {
1065
1110
  refreshSettings();
1111
+ if (typeof refreshPortalIndicator === "function") refreshPortalIndicator();
1066
1112
  }
1067
1113
  } catch {
1068
1114
  btn.textContent = "Failed";
@@ -1510,15 +1556,11 @@ function escSettings(str) {
1510
1556
  // Event listeners
1511
1557
  // ---------------------------------------------------------------------------
1512
1558
 
1513
- document.getElementById("settings-close").addEventListener("click", closeSettings);
1514
- document.getElementById("settings-overlay").addEventListener("click", (e) => {
1515
- if (e.target.id === "settings-overlay") closeSettings();
1516
- });
1517
-
1518
- document.getElementById("btn-setup-settings").addEventListener("click", () => openSettings());
1559
+ document.getElementById("btn-setup-settings")?.addEventListener("click", () => openSettings());
1519
1560
 
1520
1561
  document.addEventListener("keydown", (e) => {
1521
- if (e.key === "Escape" && !document.getElementById("settings-overlay").classList.contains("hidden")) {
1562
+ const settingsTabActive = document.querySelector('.workspace-tab[data-ws-tab="settings"].active');
1563
+ if (e.key === "Escape" && settingsTabActive) {
1522
1564
  closeSettings();
1523
1565
  }
1524
1566
  });