vibespot 0.5.2 → 0.7.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/dist/index.js +779 -180
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/ui/chat.js +152 -18
- package/ui/dashboard.js +189 -0
- package/ui/index.html +30 -9
- package/ui/settings.js +10 -7
- package/ui/setup.js +116 -11
- package/ui/styles.css +208 -75
package/ui/setup.js
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/* Theme init — runs synchronously before DOM to prevent flash */
|
|
2
|
+
(function initTheme() {
|
|
3
|
+
const stored = localStorage.getItem("vibespot-theme");
|
|
4
|
+
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
5
|
+
const theme = stored || (prefersDark ? "dark" : "light");
|
|
6
|
+
document.documentElement.setAttribute("data-theme", theme);
|
|
7
|
+
})();
|
|
8
|
+
|
|
9
|
+
function toggleTheme() {
|
|
10
|
+
const current = document.documentElement.getAttribute("data-theme") || "dark";
|
|
11
|
+
const next = current === "dark" ? "light" : "dark";
|
|
12
|
+
document.documentElement.setAttribute("data-theme", next);
|
|
13
|
+
localStorage.setItem("vibespot-theme", next);
|
|
14
|
+
}
|
|
15
|
+
|
|
1
16
|
/**
|
|
2
17
|
* Setup screen — onboarding flow in the browser.
|
|
3
18
|
* Handles theme creation, fetching, opening, and session resume.
|
|
@@ -210,6 +225,15 @@ function populateProjectRail(info) {
|
|
|
210
225
|
<span class="project-rail__item-meta">${meta}</span>`;
|
|
211
226
|
item.appendChild(infoEl);
|
|
212
227
|
|
|
228
|
+
// Double-click on name to rename
|
|
229
|
+
const nameSpan = infoEl.querySelector(".project-rail__item-name");
|
|
230
|
+
if (nameSpan) {
|
|
231
|
+
nameSpan.addEventListener("dblclick", (e) => {
|
|
232
|
+
e.stopPropagation();
|
|
233
|
+
startInlineRename(nameSpan, p);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
213
237
|
// Delete button (visible when expanded + hover)
|
|
214
238
|
const delBtn = document.createElement("button");
|
|
215
239
|
delBtn.className = "project-rail__item-delete";
|
|
@@ -248,8 +272,12 @@ function populateProjectRail(info) {
|
|
|
248
272
|
railTooltip.classList.remove("project-rail__tooltip--visible");
|
|
249
273
|
});
|
|
250
274
|
|
|
251
|
-
// Click to open
|
|
275
|
+
// Click to open (blocked while AI is generating)
|
|
252
276
|
item.addEventListener("click", () => {
|
|
277
|
+
if (typeof isStreaming !== "undefined" && isStreaming) {
|
|
278
|
+
showError("Cannot switch projects while AI is generating.");
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
253
281
|
if (p.sessionId) resumeSession(p.sessionId);
|
|
254
282
|
else openTheme(p.name);
|
|
255
283
|
});
|
|
@@ -272,6 +300,84 @@ document.getElementById("project-rail-add")?.addEventListener("click", () => {
|
|
|
272
300
|
showSetup();
|
|
273
301
|
});
|
|
274
302
|
|
|
303
|
+
// ---------------------------------------------------------------------------
|
|
304
|
+
// Inline rename
|
|
305
|
+
// ---------------------------------------------------------------------------
|
|
306
|
+
|
|
307
|
+
function startInlineRename(nameSpan, project) {
|
|
308
|
+
if (nameSpan.contentEditable === "true") return; // already editing
|
|
309
|
+
|
|
310
|
+
const oldName = project.name;
|
|
311
|
+
nameSpan.contentEditable = "true";
|
|
312
|
+
nameSpan.classList.add("project-rail__item-name--editing");
|
|
313
|
+
nameSpan.focus();
|
|
314
|
+
|
|
315
|
+
// Select all text
|
|
316
|
+
const range = document.createRange();
|
|
317
|
+
range.selectNodeContents(nameSpan);
|
|
318
|
+
const sel = window.getSelection();
|
|
319
|
+
sel.removeAllRanges();
|
|
320
|
+
sel.addRange(range);
|
|
321
|
+
|
|
322
|
+
function commit() {
|
|
323
|
+
nameSpan.contentEditable = "false";
|
|
324
|
+
nameSpan.classList.remove("project-rail__item-name--editing");
|
|
325
|
+
|
|
326
|
+
const newName = nameSpan.textContent.trim();
|
|
327
|
+
if (!newName || newName === oldName) {
|
|
328
|
+
nameSpan.textContent = oldName;
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
fetch("/api/themes/rename", {
|
|
333
|
+
method: "POST",
|
|
334
|
+
headers: { "Content-Type": "application/json" },
|
|
335
|
+
body: JSON.stringify({ sessionId: project.sessionId, newName }),
|
|
336
|
+
})
|
|
337
|
+
.then((r) => r.json())
|
|
338
|
+
.then((data) => {
|
|
339
|
+
if (data.ok) {
|
|
340
|
+
// Update the in-memory project name + UI
|
|
341
|
+
project.name = data.newName;
|
|
342
|
+
nameSpan.textContent = data.newName;
|
|
343
|
+
const item = nameSpan.closest(".project-rail__item");
|
|
344
|
+
if (item) {
|
|
345
|
+
item.dataset.name = data.newName;
|
|
346
|
+
const bubble = item.querySelector(".project-rail__item-bubble");
|
|
347
|
+
if (bubble) bubble.textContent = data.newName.charAt(0).toUpperCase();
|
|
348
|
+
}
|
|
349
|
+
// Update topbar if this is the active project
|
|
350
|
+
if (currentAppTheme === oldName) {
|
|
351
|
+
currentAppTheme = data.newName;
|
|
352
|
+
const themeNameEl = document.getElementById("theme-name");
|
|
353
|
+
if (themeNameEl) themeNameEl.textContent = data.newName;
|
|
354
|
+
window.location.hash = "#/app/" + encodeURIComponent(data.newName);
|
|
355
|
+
}
|
|
356
|
+
updateRailActive();
|
|
357
|
+
} else {
|
|
358
|
+
nameSpan.textContent = oldName;
|
|
359
|
+
showError(data.error || "Rename failed");
|
|
360
|
+
}
|
|
361
|
+
})
|
|
362
|
+
.catch(() => {
|
|
363
|
+
nameSpan.textContent = oldName;
|
|
364
|
+
showError("Rename failed");
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
nameSpan.addEventListener("blur", commit, { once: true });
|
|
369
|
+
nameSpan.addEventListener("keydown", (e) => {
|
|
370
|
+
if (e.key === "Enter") {
|
|
371
|
+
e.preventDefault();
|
|
372
|
+
nameSpan.blur();
|
|
373
|
+
}
|
|
374
|
+
if (e.key === "Escape") {
|
|
375
|
+
nameSpan.textContent = oldName;
|
|
376
|
+
nameSpan.blur();
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
275
381
|
// ---------------------------------------------------------------------------
|
|
276
382
|
// Delete project confirmation
|
|
277
383
|
// ---------------------------------------------------------------------------
|
|
@@ -699,20 +805,19 @@ function showSetup() {
|
|
|
699
805
|
initSetup();
|
|
700
806
|
}
|
|
701
807
|
|
|
702
|
-
//
|
|
808
|
+
// App back button → go back to dashboard from chat
|
|
809
|
+
document.getElementById("app-back")?.addEventListener("click", () => {
|
|
810
|
+
if (currentAppTheme && typeof showDashboard === "function") {
|
|
811
|
+
appScreen.classList.add("hidden");
|
|
812
|
+
showDashboard(currentAppTheme);
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// Logo click → go back to setup (from dashboard)
|
|
703
817
|
document.querySelectorAll(".topbar__brand").forEach((el) => {
|
|
704
818
|
el.style.cursor = "pointer";
|
|
705
819
|
el.addEventListener("click", () => {
|
|
706
820
|
const dashEl = document.getElementById("dashboard-screen");
|
|
707
|
-
// From chat → go to dashboard
|
|
708
|
-
if (!appScreen.classList.contains("hidden") && currentAppTheme) {
|
|
709
|
-
if (typeof showDashboard === "function") {
|
|
710
|
-
appScreen.classList.add("hidden");
|
|
711
|
-
showDashboard(currentAppTheme);
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
// From dashboard → go to setup
|
|
716
821
|
if (dashEl && !dashEl.classList.contains("hidden")) {
|
|
717
822
|
showSetup();
|
|
718
823
|
return;
|