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/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
- // Logo click → go back to dashboard (from chat) or setup (from dashboard)
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;