vibespot 0.4.0 → 0.4.3

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.0",
3
+ "version": "0.4.3",
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
@@ -78,13 +78,19 @@ function handleWsMessage(msg) {
78
78
  statusEngine.textContent = msg.engine || "";
79
79
  fetchHsAccountStatus();
80
80
 
81
+ // Populate chat header
82
+ const chatHeaderTitle = document.getElementById("chat-header-title");
83
+ const chatHeaderContext = document.getElementById("chat-header-context");
84
+ if (chatHeaderTitle) chatHeaderTitle.textContent = msg.themeName || "Chat";
85
+ if (chatHeaderContext) chatHeaderContext.textContent = msg.engine || "";
86
+
81
87
  // Restore chat history from server
82
88
  if (msg.messages && msg.messages.length > 0) {
83
89
  for (const m of msg.messages) {
84
90
  if (m.role === "user") {
85
- appendUserMessage(m.content);
91
+ appendUserMessage(m.content, m.timestamp);
86
92
  } else if (m.role === "assistant") {
87
- appendRestoredAssistantMessage(m.content);
93
+ appendRestoredAssistantMessage(m.content, m.timestamp);
88
94
  }
89
95
  }
90
96
  scrollToBottom();
@@ -167,10 +173,25 @@ function sendMessage(text) {
167
173
  // Message rendering
168
174
  // ---------------------------------------------------------------------------
169
175
 
170
- function appendUserMessage(text) {
176
+ function formatMessageTime(ts) {
177
+ if (!ts) return "";
178
+ const d = new Date(ts);
179
+ return `${d.getHours()}:${d.getMinutes().toString().padStart(2, "0")}`;
180
+ }
181
+
182
+ function appendUserMessage(text, timestamp) {
183
+ const time = formatMessageTime(timestamp || Date.now());
171
184
  const div = document.createElement("div");
172
185
  div.className = "chat-msg chat-msg--user";
173
- div.innerHTML = `<div class="chat-msg__bubble">${escapeHtml(text)}</div>`;
186
+ div.innerHTML = `
187
+ <div class="chat-msg__content">
188
+ <div class="chat-msg__header">
189
+ <span class="chat-msg__sender">You</span>
190
+ <span class="chat-msg__time">${time}</span>
191
+ </div>
192
+ <div class="chat-msg__bubble">${escapeHtml(text)}</div>
193
+ </div>
194
+ <div class="chat-msg__avatar chat-msg__avatar--user">Y</div>`;
174
195
  messagesEl.appendChild(div);
175
196
  scrollToBottom();
176
197
  }
@@ -186,9 +207,18 @@ function startStreaming() {
186
207
  showGeneratingPreview();
187
208
  }
188
209
 
210
+ const time = formatMessageTime(streamStartTime);
189
211
  const div = document.createElement("div");
190
212
  div.className = "chat-msg chat-msg--assistant chat-msg--streaming";
191
- div.innerHTML = `<div class="chat-msg__bubble"></div>`;
213
+ div.innerHTML = `
214
+ <div class="chat-msg__avatar chat-msg__avatar--ai">AI</div>
215
+ <div class="chat-msg__content">
216
+ <div class="chat-msg__header">
217
+ <span class="chat-msg__sender">vibeSpot AI</span>
218
+ <span class="chat-msg__time">${time}</span>
219
+ </div>
220
+ <div class="chat-msg__bubble"></div>
221
+ </div>`;
192
222
  messagesEl.appendChild(div);
193
223
  streamingMsgEl = div.querySelector(".chat-msg__bubble");
194
224
  scrollToBottom();
@@ -271,11 +301,12 @@ function finishStreaming() {
271
301
  if (streamingEl) {
272
302
  streamingEl.classList.remove("chat-msg--streaming");
273
303
 
274
- // Add duration metadata beneath the bubble
304
+ // Add duration metadata beneath the bubble (inside .chat-msg__content)
275
305
  const meta = document.createElement("div");
276
306
  meta.className = "chat-msg__meta";
277
307
  meta.textContent = durationStr;
278
- streamingEl.appendChild(meta);
308
+ const contentEl = streamingEl.querySelector(".chat-msg__content") || streamingEl;
309
+ contentEl.appendChild(meta);
279
310
  }
280
311
 
281
312
  // Final render of the full response
@@ -290,11 +321,20 @@ function finishStreaming() {
290
321
  }
291
322
 
292
323
  function appendAssistantError(message) {
324
+ const time = formatMessageTime(Date.now());
293
325
  const div = document.createElement("div");
294
326
  div.className = "chat-msg chat-msg--assistant";
295
- div.innerHTML = `<div class="chat-msg__bubble" style="border-left: 3px solid var(--error);">
296
- <strong>Error:</strong> ${escapeHtml(message)}
297
- </div>`;
327
+ div.innerHTML = `
328
+ <div class="chat-msg__avatar chat-msg__avatar--ai">AI</div>
329
+ <div class="chat-msg__content">
330
+ <div class="chat-msg__header">
331
+ <span class="chat-msg__sender">vibeSpot AI</span>
332
+ <span class="chat-msg__time">${time}</span>
333
+ </div>
334
+ <div class="chat-msg__bubble" style="border-left: 3px solid var(--error);">
335
+ <strong>Error:</strong> ${escapeHtml(message)}
336
+ </div>
337
+ </div>`;
298
338
  messagesEl.appendChild(div);
299
339
  scrollToBottom();
300
340
  }
@@ -359,10 +399,16 @@ function setStatus(text) {
359
399
  // Restored / system messages
360
400
  // ---------------------------------------------------------------------------
361
401
 
362
- function appendRestoredAssistantMessage(text) {
402
+ function appendRestoredAssistantMessage(text, timestamp) {
403
+ const time = formatMessageTime(timestamp);
363
404
  const div = document.createElement("div");
364
405
  div.className = "chat-msg chat-msg--assistant";
365
- div.innerHTML = `<div class="chat-msg__bubble">${renderMarkdown(text)}</div>`;
406
+ div.innerHTML = `
407
+ <div class="chat-msg__avatar chat-msg__avatar--ai">AI</div>
408
+ <div class="chat-msg__content">
409
+ ${time ? `<div class="chat-msg__header"><span class="chat-msg__sender">vibeSpot AI</span><span class="chat-msg__time">${time}</span></div>` : ""}
410
+ <div class="chat-msg__bubble">${renderMarkdown(text)}</div>
411
+ </div>`;
366
412
  messagesEl.appendChild(div);
367
413
  }
368
414
 
@@ -713,6 +759,12 @@ document.getElementById("starter-templates").addEventListener("click", (e) => {
713
759
  if (btn) sendMessage(btn.dataset.prompt);
714
760
  });
715
761
 
762
+ // Templates icon in input area — toggle welcome section visibility
763
+ document.getElementById("btn-starter-templates")?.addEventListener("click", () => {
764
+ const welcome = document.getElementById("chat-welcome");
765
+ if (welcome) welcome.classList.toggle("hidden");
766
+ });
767
+
716
768
  // Version history
717
769
  document.getElementById("btn-history")?.addEventListener("click", toggleHistoryPanel);
718
770
  document.getElementById("history-panel-close")?.addEventListener("click", () => {
package/ui/index.html CHANGED
@@ -288,6 +288,10 @@
288
288
  </div>
289
289
 
290
290
  <div class="chat">
291
+ <div class="chat__header" id="chat-header">
292
+ <span class="chat__header-title" id="chat-header-title">Chat</span>
293
+ <span class="chat__header-context" id="chat-header-context"></span>
294
+ </div>
291
295
  <div class="chat__messages" id="chat-messages">
292
296
  <div class="chat__welcome" id="chat-welcome">
293
297
  <p>Describe what you want or pick a template to get started.</p>
@@ -302,10 +306,17 @@
302
306
  </div>
303
307
  </div>
304
308
  <div class="chat__input-area">
305
- <textarea class="chat__input" id="chat-input" placeholder="Describe your landing page..." rows="1"></textarea>
306
- <button class="chat__send" id="chat-send" title="Send (Enter)">
307
- <svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M3 10L17 3L10 17L9 11L3 10Z" fill="currentColor"/></svg>
308
- </button>
309
+ <div class="chat__input-wrapper">
310
+ <textarea class="chat__input" id="chat-input" placeholder="Describe your landing page..." rows="1"></textarea>
311
+ <div class="chat__input-actions">
312
+ <button class="chat__input-icon" id="btn-starter-templates" title="Templates">
313
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M9 21V9"/></svg>
314
+ </button>
315
+ <button class="chat__send" id="chat-send" title="Send (Enter)">
316
+ <svg width="18" height="18" viewBox="0 0 20 20" fill="none"><path d="M3 10L17 3L10 17L9 11L3 10Z" fill="currentColor"/></svg>
317
+ </button>
318
+ </div>
319
+ </div>
309
320
  </div>
310
321
  </div>
311
322
  </aside>
package/ui/styles.css CHANGED
@@ -1221,6 +1221,26 @@ html, body {
1221
1221
  overflow: hidden;
1222
1222
  }
1223
1223
 
1224
+ .chat__header {
1225
+ display: flex;
1226
+ align-items: baseline;
1227
+ gap: 8px;
1228
+ padding: 12px 16px;
1229
+ border-bottom: 1px solid var(--border);
1230
+ background: var(--bg-panel-solid);
1231
+ flex-shrink: 0;
1232
+ }
1233
+ .chat__header-title {
1234
+ font-family: var(--font-display);
1235
+ font-size: 15px;
1236
+ font-weight: 600;
1237
+ color: var(--text);
1238
+ }
1239
+ .chat__header-context {
1240
+ font-size: 12px;
1241
+ color: var(--text-dim);
1242
+ }
1243
+
1224
1244
  .chat__messages {
1225
1245
  flex: 1;
1226
1246
  overflow-y: auto;
@@ -1310,6 +1330,9 @@ html, body {
1310
1330
 
1311
1331
  /* Chat messages */
1312
1332
  .chat-msg {
1333
+ display: flex;
1334
+ align-items: flex-start;
1335
+ gap: 10px;
1313
1336
  margin-bottom: 16px;
1314
1337
  animation: fadeIn 0.2s ease;
1315
1338
  }
@@ -1320,32 +1343,100 @@ html, body {
1320
1343
  }
1321
1344
 
1322
1345
  .chat-msg--user {
1323
- display: flex;
1324
- justify-content: flex-end;
1346
+ flex-direction: row-reverse;
1347
+ }
1348
+
1349
+ .chat-msg--user .chat-msg__header {
1350
+ flex-direction: row-reverse;
1325
1351
  }
1326
1352
 
1327
1353
  .chat-msg--user .chat-msg__bubble {
1328
1354
  background: var(--accent-gradient);
1329
1355
  color: white;
1330
- border-radius: var(--radius) var(--radius) 2px var(--radius);
1356
+ border-radius: 16px 16px 4px 16px;
1331
1357
  max-width: 85%;
1332
1358
  }
1333
1359
 
1334
1360
  .chat-msg--assistant .chat-msg__bubble {
1335
1361
  background: var(--bg-input);
1336
1362
  color: var(--text);
1337
- border-radius: var(--radius) var(--radius) var(--radius) 2px;
1363
+ border-radius: 16px 16px 16px 4px;
1338
1364
  max-width: 95%;
1339
1365
  border: 1px solid var(--border);
1340
1366
  }
1341
1367
 
1368
+ /* Avatar */
1369
+ .chat-msg__avatar {
1370
+ width: 32px;
1371
+ height: 32px;
1372
+ border-radius: 50%;
1373
+ display: flex;
1374
+ align-items: center;
1375
+ justify-content: center;
1376
+ font-size: 12px;
1377
+ font-weight: 600;
1378
+ flex-shrink: 0;
1379
+ font-family: var(--font-display);
1380
+ }
1381
+ .chat-msg__avatar--user {
1382
+ background: var(--accent-gradient);
1383
+ color: white;
1384
+ }
1385
+ .chat-msg__avatar--ai {
1386
+ background: var(--accent-dim);
1387
+ color: var(--accent);
1388
+ border: 1px solid var(--accent-glow);
1389
+ }
1390
+
1391
+ /* Content wrapper */
1392
+ .chat-msg__content {
1393
+ flex: 1;
1394
+ min-width: 0;
1395
+ }
1396
+
1397
+ /* Header (sender + time) */
1398
+ .chat-msg__header {
1399
+ display: flex;
1400
+ align-items: baseline;
1401
+ gap: 8px;
1402
+ margin-bottom: 4px;
1403
+ padding: 0 2px;
1404
+ }
1405
+ .chat-msg__sender {
1406
+ font-size: 12px;
1407
+ font-weight: 600;
1408
+ color: var(--text-dim);
1409
+ }
1410
+ .chat-msg__time {
1411
+ font-size: 11px;
1412
+ color: var(--text-muted);
1413
+ font-variant-numeric: tabular-nums;
1414
+ }
1415
+
1342
1416
  .chat-msg__bubble {
1343
- padding: 10px 14px;
1417
+ padding: 12px 16px;
1344
1418
  font-size: 13.5px;
1345
1419
  line-height: 1.6;
1346
1420
  word-wrap: break-word;
1347
1421
  }
1348
1422
 
1423
+ /* Streaming cursor */
1424
+ .chat-msg--streaming .chat-msg__bubble::after {
1425
+ content: "";
1426
+ display: inline-block;
1427
+ width: 6px;
1428
+ height: 14px;
1429
+ background: var(--accent);
1430
+ border-radius: 1px;
1431
+ margin-left: 2px;
1432
+ vertical-align: text-bottom;
1433
+ animation: cursorBlink 0.8s ease infinite;
1434
+ }
1435
+ @keyframes cursorBlink {
1436
+ 0%, 100% { opacity: 1; }
1437
+ 50% { opacity: 0.2; }
1438
+ }
1439
+
1349
1440
  .chat-msg__bubble p { margin-bottom: 8px; }
1350
1441
  .chat-msg__bubble p:last-child { margin-bottom: 0; }
1351
1442
 
@@ -1419,41 +1510,74 @@ html, body {
1419
1510
 
1420
1511
  /* Chat input */
1421
1512
  .chat__input-area {
1422
- display: flex;
1423
- align-items: flex-end;
1424
- gap: 8px;
1425
1513
  padding: 12px 16px;
1426
1514
  border-top: 1px solid var(--border);
1427
1515
  background: var(--bg-panel-solid);
1428
1516
  }
1429
1517
 
1518
+ .chat__input-wrapper {
1519
+ display: flex;
1520
+ align-items: flex-end;
1521
+ gap: 8px;
1522
+ background: var(--bg-input);
1523
+ border: 1px solid var(--border);
1524
+ border-radius: 12px;
1525
+ padding: 6px 6px 6px 14px;
1526
+ transition: border-color 0.2s;
1527
+ }
1528
+ .chat__input-wrapper:focus-within {
1529
+ border-color: var(--accent-glow);
1530
+ }
1531
+
1430
1532
  .chat__input {
1431
1533
  flex: 1;
1432
1534
  resize: none;
1433
- border: 1px solid var(--border);
1434
- border-radius: var(--radius);
1435
- padding: 10px 14px;
1436
- background: var(--bg-input);
1535
+ border: none;
1536
+ background: transparent;
1437
1537
  color: var(--text);
1438
1538
  font-family: var(--font);
1439
1539
  font-size: 14px;
1440
1540
  line-height: 1.5;
1441
1541
  max-height: 150px;
1442
1542
  outline: none;
1443
- transition: border-color 0.2s;
1543
+ padding: 6px 0;
1444
1544
  }
1445
- .chat__input:focus { border-color: var(--accent-glow); }
1446
1545
  .chat__input::placeholder { color: var(--text-muted); }
1447
1546
 
1547
+ .chat__input-actions {
1548
+ display: flex;
1549
+ align-items: center;
1550
+ gap: 4px;
1551
+ flex-shrink: 0;
1552
+ }
1553
+
1554
+ .chat__input-icon {
1555
+ width: 32px;
1556
+ height: 32px;
1557
+ display: flex;
1558
+ align-items: center;
1559
+ justify-content: center;
1560
+ background: none;
1561
+ border: none;
1562
+ color: var(--text-muted);
1563
+ cursor: pointer;
1564
+ border-radius: 8px;
1565
+ transition: all 0.15s;
1566
+ }
1567
+ .chat__input-icon:hover {
1568
+ color: var(--accent);
1569
+ background: var(--accent-tint);
1570
+ }
1571
+
1448
1572
  .chat__send {
1449
- width: 38px;
1450
- height: 38px;
1573
+ width: 32px;
1574
+ height: 32px;
1451
1575
  display: flex;
1452
1576
  align-items: center;
1453
1577
  justify-content: center;
1454
1578
  background: var(--accent-gradient);
1455
1579
  border: none;
1456
- border-radius: var(--radius);
1580
+ border-radius: 8px;
1457
1581
  color: white;
1458
1582
  cursor: pointer;
1459
1583
  transition: filter 0.15s;