Spaces:
Running
Running
minor improvements; fixes..
Browse files- static/app.js +45 -22
- static/index.html +4 -11
- static/style.css +61 -7
static/app.js
CHANGED
|
@@ -20,7 +20,7 @@ let currentPersona = localStorage.getItem('stemcopilot_persona') || 'vidyut';
|
|
| 20 |
let currentLanguage = localStorage.getItem('stemcopilot_language') || 'auto';
|
| 21 |
let currentUsername = localStorage.getItem('stemcopilot_username') || '';
|
| 22 |
|
| 23 |
-
const _PERSONA_LABELS = { vidyut: '
|
| 24 |
|
| 25 |
|
| 26 |
/* ── DOM References ─────────────────────────────────────────── */
|
|
@@ -104,7 +104,6 @@ const historyOverlayClose = document.getElementById('historyOverlayClose');
|
|
| 104 |
const overlayHistoryList = document.getElementById('overlayHistoryList');
|
| 105 |
const overlayHistoryEmpty = document.getElementById('overlayHistoryEmpty');
|
| 106 |
const overlaySettingsBtn = document.getElementById('overlaySettingsBtn');
|
| 107 |
-
const overlayProfileBtn = document.getElementById('overlayProfileBtn');
|
| 108 |
const overlayLogoutBtn = document.getElementById('overlayLogoutBtn');
|
| 109 |
const overlayUserAvatar = document.getElementById('overlayUserAvatar');
|
| 110 |
const overlayUserName = document.getElementById('overlayUserName');
|
|
@@ -473,23 +472,14 @@ if (overlaySettingsBtn) overlaySettingsBtn.addEventListener('click', () => {
|
|
| 473 |
settingsOverlay.classList.add('show');
|
| 474 |
});
|
| 475 |
|
| 476 |
-
if (overlayProfileBtn) overlayProfileBtn.addEventListener('click', () => {
|
| 477 |
-
_closeAllOverlays();
|
| 478 |
-
// Open settings on Profile tab
|
| 479 |
-
document.querySelectorAll('.settings-nav-btn').forEach(b => b.classList.remove('active'));
|
| 480 |
-
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
| 481 |
-
const btn = document.querySelector('.settings-nav-btn[data-tab="personalization"]');
|
| 482 |
-
const tab = document.getElementById('tab-personalization');
|
| 483 |
-
if (btn) btn.classList.add('active');
|
| 484 |
-
if (tab) tab.classList.add('active');
|
| 485 |
-
settingsOverlay.classList.add('show');
|
| 486 |
-
});
|
| 487 |
|
| 488 |
if (overlayLogoutBtn) overlayLogoutBtn.addEventListener('click', () => {
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
|
|
|
|
|
|
| 493 |
});
|
| 494 |
|
| 495 |
function _renderOverlayHistory() {
|
|
@@ -501,16 +491,49 @@ function _renderOverlayHistory() {
|
|
| 501 |
}
|
| 502 |
if (overlayHistoryEmpty) overlayHistoryEmpty.style.display = 'none';
|
| 503 |
threads.forEach(thread => {
|
| 504 |
-
const item = document.createElement('
|
| 505 |
item.className = `overlay-history-item ${thread.id === currentThreadId ? 'active' : ''}`;
|
| 506 |
item.innerHTML = `
|
| 507 |
-
<
|
| 508 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 509 |
`;
|
| 510 |
-
item.addEventListener('click', () => {
|
| 511 |
_closeAllOverlays();
|
| 512 |
loadThread(thread.id);
|
| 513 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 514 |
overlayHistoryList.appendChild(item);
|
| 515 |
});
|
| 516 |
}
|
|
@@ -770,7 +793,7 @@ if (userProfileBtn) userProfileBtn.addEventListener('click', (e) => { e.stopProp
|
|
| 770 |
if (openSettingsBtn) openSettingsBtn.addEventListener('click', () => { userMenu.classList.remove('show'); settingsOverlay.classList.add('show'); });
|
| 771 |
if (settingsCloseBtn) settingsCloseBtn.addEventListener('click', () => settingsOverlay.classList.remove('show'));
|
| 772 |
if (settingsOverlay) settingsOverlay.addEventListener('click', (e) => { if (e.target === settingsOverlay) settingsOverlay.classList.remove('show'); });
|
| 773 |
-
if (logoutBtn) logoutBtn.addEventListener('click', () => { localStorage.removeItem('stemcopilot_user'); localStorage.removeItem('stemcopilot_username'); currentUser = null; location.reload(); });
|
| 774 |
|
| 775 |
document.querySelectorAll('.settings-nav-btn').forEach(btn => {
|
| 776 |
btn.addEventListener('click', () => {
|
|
|
|
| 20 |
let currentLanguage = localStorage.getItem('stemcopilot_language') || 'auto';
|
| 21 |
let currentUsername = localStorage.getItem('stemcopilot_username') || '';
|
| 22 |
|
| 23 |
+
const _PERSONA_LABELS = { vidyut: 'Guru', nerd: 'Nerd', noob: 'Beginner', thoughtful: 'Thoughtful', panic: 'Panic' };
|
| 24 |
|
| 25 |
|
| 26 |
/* ── DOM References ─────────────────────────────────────────── */
|
|
|
|
| 104 |
const overlayHistoryList = document.getElementById('overlayHistoryList');
|
| 105 |
const overlayHistoryEmpty = document.getElementById('overlayHistoryEmpty');
|
| 106 |
const overlaySettingsBtn = document.getElementById('overlaySettingsBtn');
|
|
|
|
| 107 |
const overlayLogoutBtn = document.getElementById('overlayLogoutBtn');
|
| 108 |
const overlayUserAvatar = document.getElementById('overlayUserAvatar');
|
| 109 |
const overlayUserName = document.getElementById('overlayUserName');
|
|
|
|
| 472 |
settingsOverlay.classList.add('show');
|
| 473 |
});
|
| 474 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
|
| 476 |
if (overlayLogoutBtn) overlayLogoutBtn.addEventListener('click', () => {
|
| 477 |
+
if (confirm('Do you want to log out?')) {
|
| 478 |
+
localStorage.removeItem('stemcopilot_user');
|
| 479 |
+
localStorage.removeItem('stemcopilot_username');
|
| 480 |
+
currentUser = null;
|
| 481 |
+
location.reload();
|
| 482 |
+
}
|
| 483 |
});
|
| 484 |
|
| 485 |
function _renderOverlayHistory() {
|
|
|
|
| 491 |
}
|
| 492 |
if (overlayHistoryEmpty) overlayHistoryEmpty.style.display = 'none';
|
| 493 |
threads.forEach(thread => {
|
| 494 |
+
const item = document.createElement('div');
|
| 495 |
item.className = `overlay-history-item ${thread.id === currentThreadId ? 'active' : ''}`;
|
| 496 |
item.innerHTML = `
|
| 497 |
+
<div class="overlay-history-main">
|
| 498 |
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
| 499 |
+
<span class="overlay-history-title">${escapeHtml(thread.title)}</span>
|
| 500 |
+
</div>
|
| 501 |
+
<div class="overlay-history-actions">
|
| 502 |
+
<button class="overlay-action-btn" data-action="rename" title="Rename">
|
| 503 |
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"/></svg>
|
| 504 |
+
</button>
|
| 505 |
+
<button class="overlay-action-btn overlay-action-delete" data-action="delete" title="Delete">
|
| 506 |
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
|
| 507 |
+
</button>
|
| 508 |
+
</div>
|
| 509 |
`;
|
| 510 |
+
item.querySelector('.overlay-history-main').addEventListener('click', () => {
|
| 511 |
_closeAllOverlays();
|
| 512 |
loadThread(thread.id);
|
| 513 |
});
|
| 514 |
+
item.querySelector('[data-action="rename"]').addEventListener('click', (e) => {
|
| 515 |
+
e.stopPropagation();
|
| 516 |
+
const newTitle = prompt('Rename chat:', thread.title);
|
| 517 |
+
if (newTitle && newTitle.trim()) {
|
| 518 |
+
fetch('/rename', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ thread_id: thread.id, title: newTitle.trim() }) })
|
| 519 |
+
.then(() => { thread.title = newTitle.trim(); _renderOverlayHistory(); renderHistory(); })
|
| 520 |
+
.catch(() => showToast('Failed to rename.', 'error'));
|
| 521 |
+
}
|
| 522 |
+
});
|
| 523 |
+
item.querySelector('[data-action="delete"]').addEventListener('click', (e) => {
|
| 524 |
+
e.stopPropagation();
|
| 525 |
+
if (confirm('Delete this chat?')) {
|
| 526 |
+
fetch('/thread/' + thread.id, { method: 'DELETE' })
|
| 527 |
+
.then(() => {
|
| 528 |
+
const idx = threads.findIndex(t => t.id === thread.id);
|
| 529 |
+
if (idx !== -1) threads.splice(idx, 1);
|
| 530 |
+
if (thread.id === currentThreadId) startNewChat();
|
| 531 |
+
_renderOverlayHistory();
|
| 532 |
+
renderHistory();
|
| 533 |
+
})
|
| 534 |
+
.catch(() => showToast('Failed to delete.', 'error'));
|
| 535 |
+
}
|
| 536 |
+
});
|
| 537 |
overlayHistoryList.appendChild(item);
|
| 538 |
});
|
| 539 |
}
|
|
|
|
| 793 |
if (openSettingsBtn) openSettingsBtn.addEventListener('click', () => { userMenu.classList.remove('show'); settingsOverlay.classList.add('show'); });
|
| 794 |
if (settingsCloseBtn) settingsCloseBtn.addEventListener('click', () => settingsOverlay.classList.remove('show'));
|
| 795 |
if (settingsOverlay) settingsOverlay.addEventListener('click', (e) => { if (e.target === settingsOverlay) settingsOverlay.classList.remove('show'); });
|
| 796 |
+
if (logoutBtn) logoutBtn.addEventListener('click', () => { if (confirm('Do you want to log out?')) { localStorage.removeItem('stemcopilot_user'); localStorage.removeItem('stemcopilot_username'); currentUser = null; location.reload(); } });
|
| 797 |
|
| 798 |
document.querySelectorAll('.settings-nav-btn').forEach(btn => {
|
| 799 |
btn.addEventListener('click', () => {
|
static/index.html
CHANGED
|
@@ -69,7 +69,7 @@
|
|
| 69 |
<span>Sign in with Google</span>
|
| 70 |
</button>
|
| 71 |
</div>
|
| 72 |
-
<p class="login-footer">By signing in, you agree to our Terms of Service.</p>
|
| 73 |
</div>
|
| 74 |
</div>
|
| 75 |
|
|
@@ -150,13 +150,6 @@
|
|
| 150 |
</svg>
|
| 151 |
Settings
|
| 152 |
</button>
|
| 153 |
-
<button class="overlay-menu-item" id="overlayProfileBtn">
|
| 154 |
-
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
| 155 |
-
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
|
| 156 |
-
<circle cx="12" cy="7" r="4" />
|
| 157 |
-
</svg>
|
| 158 |
-
Profile
|
| 159 |
-
</button>
|
| 160 |
<button class="overlay-menu-item overlay-menu-logout" id="overlayLogoutBtn">
|
| 161 |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
| 162 |
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
|
@@ -300,7 +293,7 @@
|
|
| 300 |
</button>
|
| 301 |
<div class="style-dropdown" id="heroStyleDropdown">
|
| 302 |
<div class="style-option active" data-persona="vidyut">
|
| 303 |
-
<div class="style-option-name">
|
| 304 |
<div class="style-option-desc">Calm, clear, step-by-step master teacher. Makes hard concepts obvious.</div>
|
| 305 |
</div>
|
| 306 |
<div class="style-option" data-persona="nerd">
|
|
@@ -353,12 +346,12 @@
|
|
| 353 |
<!-- Teaching style selector -->
|
| 354 |
<div class="style-selector-wrap" id="styleSelectorWrap">
|
| 355 |
<button class="style-selector-btn" id="styleSelectorBtn" title="Teaching style">
|
| 356 |
-
<span id="styleSelectorLabel">
|
| 357 |
<svg viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9" /></svg>
|
| 358 |
</button>
|
| 359 |
<div class="style-dropdown" id="styleDropdown">
|
| 360 |
<div class="style-option active" data-persona="vidyut">
|
| 361 |
-
<div class="style-option-name">
|
| 362 |
<div class="style-option-desc">Calm, clear, step-by-step master teacher. Makes hard concepts obvious.</div>
|
| 363 |
</div>
|
| 364 |
<div class="style-option" data-persona="nerd">
|
|
|
|
| 69 |
<span>Sign in with Google</span>
|
| 70 |
</button>
|
| 71 |
</div>
|
| 72 |
+
<p class="login-footer">By signing in, you agree to our <a href="https://www.stembotix.com/terms-policy" target="_blank" rel="noopener" class="terms-link">Terms of Service</a>.</p>
|
| 73 |
</div>
|
| 74 |
</div>
|
| 75 |
|
|
|
|
| 150 |
</svg>
|
| 151 |
Settings
|
| 152 |
</button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
<button class="overlay-menu-item overlay-menu-logout" id="overlayLogoutBtn">
|
| 154 |
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
| 155 |
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
|
|
|
|
| 293 |
</button>
|
| 294 |
<div class="style-dropdown" id="heroStyleDropdown">
|
| 295 |
<div class="style-option active" data-persona="vidyut">
|
| 296 |
+
<div class="style-option-name">Guru</div>
|
| 297 |
<div class="style-option-desc">Calm, clear, step-by-step master teacher. Makes hard concepts obvious.</div>
|
| 298 |
</div>
|
| 299 |
<div class="style-option" data-persona="nerd">
|
|
|
|
| 346 |
<!-- Teaching style selector -->
|
| 347 |
<div class="style-selector-wrap" id="styleSelectorWrap">
|
| 348 |
<button class="style-selector-btn" id="styleSelectorBtn" title="Teaching style">
|
| 349 |
+
<span id="styleSelectorLabel">Guru</span>
|
| 350 |
<svg viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9" /></svg>
|
| 351 |
</button>
|
| 352 |
<div class="style-dropdown" id="styleDropdown">
|
| 353 |
<div class="style-option active" data-persona="vidyut">
|
| 354 |
+
<div class="style-option-name">Guru</div>
|
| 355 |
<div class="style-option-desc">Calm, clear, step-by-step master teacher. Makes hard concepts obvious.</div>
|
| 356 |
</div>
|
| 357 |
<div class="style-option" data-persona="nerd">
|
static/style.css
CHANGED
|
@@ -211,11 +211,23 @@ textarea, input, select {
|
|
| 211 |
}
|
| 212 |
|
| 213 |
.login-footer {
|
| 214 |
-
font-size:
|
| 215 |
-
color: var(--text-
|
| 216 |
margin-top: 16px;
|
| 217 |
}
|
| 218 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 219 |
|
| 220 |
/* ============================================================
|
| 221 |
BYOK ONBOARDING
|
|
@@ -583,14 +595,15 @@ textarea, input, select {
|
|
| 583 |
}
|
| 584 |
|
| 585 |
.overlay-history-item {
|
| 586 |
-
padding:
|
| 587 |
border-radius: 10px;
|
| 588 |
cursor: pointer;
|
| 589 |
color: var(--text-secondary);
|
| 590 |
font-size: 14px;
|
| 591 |
display: flex;
|
| 592 |
align-items: center;
|
| 593 |
-
|
|
|
|
| 594 |
transition: background 0.2s, color 0.2s;
|
| 595 |
border: none;
|
| 596 |
background: none;
|
|
@@ -599,6 +612,47 @@ textarea, input, select {
|
|
| 599 |
font-family: inherit;
|
| 600 |
}
|
| 601 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 602 |
.overlay-history-item:hover,
|
| 603 |
.overlay-history-item:active {
|
| 604 |
background: var(--bg-hover);
|
|
@@ -1019,8 +1073,8 @@ textarea, input, select {
|
|
| 1019 |
}
|
| 1020 |
|
| 1021 |
.hero-bot-icon {
|
| 1022 |
-
width:
|
| 1023 |
-
height:
|
| 1024 |
object-fit: contain;
|
| 1025 |
filter: drop-shadow(0 8px 24px var(--brand-glow));
|
| 1026 |
}
|
|
@@ -2066,7 +2120,7 @@ textarea, input, select {
|
|
| 2066 |
}
|
| 2067 |
|
| 2068 |
.hero-title { font-size: 22px; }
|
| 2069 |
-
.hero-bot-icon { width:
|
| 2070 |
|
| 2071 |
.hero-welcome {
|
| 2072 |
justify-content: center;
|
|
|
|
| 211 |
}
|
| 212 |
|
| 213 |
.login-footer {
|
| 214 |
+
font-size: 13px;
|
| 215 |
+
color: var(--text-secondary);
|
| 216 |
margin-top: 16px;
|
| 217 |
}
|
| 218 |
|
| 219 |
+
.terms-link {
|
| 220 |
+
color: #EC642B;
|
| 221 |
+
font-weight: 600;
|
| 222 |
+
text-decoration: underline;
|
| 223 |
+
text-underline-offset: 2px;
|
| 224 |
+
transition: opacity 0.2s;
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
.terms-link:hover { opacity: 0.8; }
|
| 228 |
+
|
| 229 |
+
[data-theme="light"] .terms-link { color: #2C2872; }
|
| 230 |
+
|
| 231 |
|
| 232 |
/* ============================================================
|
| 233 |
BYOK ONBOARDING
|
|
|
|
| 595 |
}
|
| 596 |
|
| 597 |
.overlay-history-item {
|
| 598 |
+
padding: 10px 14px;
|
| 599 |
border-radius: 10px;
|
| 600 |
cursor: pointer;
|
| 601 |
color: var(--text-secondary);
|
| 602 |
font-size: 14px;
|
| 603 |
display: flex;
|
| 604 |
align-items: center;
|
| 605 |
+
justify-content: space-between;
|
| 606 |
+
gap: 8px;
|
| 607 |
transition: background 0.2s, color 0.2s;
|
| 608 |
border: none;
|
| 609 |
background: none;
|
|
|
|
| 612 |
font-family: inherit;
|
| 613 |
}
|
| 614 |
|
| 615 |
+
.overlay-history-main {
|
| 616 |
+
display: flex;
|
| 617 |
+
align-items: center;
|
| 618 |
+
gap: 10px;
|
| 619 |
+
flex: 1;
|
| 620 |
+
min-width: 0;
|
| 621 |
+
cursor: pointer;
|
| 622 |
+
}
|
| 623 |
+
|
| 624 |
+
.overlay-history-actions {
|
| 625 |
+
display: flex;
|
| 626 |
+
gap: 4px;
|
| 627 |
+
flex-shrink: 0;
|
| 628 |
+
opacity: 0.5;
|
| 629 |
+
transition: opacity 0.2s;
|
| 630 |
+
}
|
| 631 |
+
|
| 632 |
+
.overlay-history-item:hover .overlay-history-actions { opacity: 1; }
|
| 633 |
+
|
| 634 |
+
.overlay-action-btn {
|
| 635 |
+
background: none;
|
| 636 |
+
border: none;
|
| 637 |
+
color: var(--text-muted);
|
| 638 |
+
cursor: pointer;
|
| 639 |
+
padding: 4px;
|
| 640 |
+
border-radius: 6px;
|
| 641 |
+
display: flex;
|
| 642 |
+
align-items: center;
|
| 643 |
+
justify-content: center;
|
| 644 |
+
transition: background 0.2s, color 0.2s;
|
| 645 |
+
}
|
| 646 |
+
|
| 647 |
+
.overlay-action-btn:hover {
|
| 648 |
+
background: var(--bg-hover);
|
| 649 |
+
color: var(--text-primary);
|
| 650 |
+
}
|
| 651 |
+
|
| 652 |
+
.overlay-action-delete:hover {
|
| 653 |
+
color: #ff4a4a;
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
.overlay-history-item:hover,
|
| 657 |
.overlay-history-item:active {
|
| 658 |
background: var(--bg-hover);
|
|
|
|
| 1073 |
}
|
| 1074 |
|
| 1075 |
.hero-bot-icon {
|
| 1076 |
+
width: 160px;
|
| 1077 |
+
height: 160px;
|
| 1078 |
object-fit: contain;
|
| 1079 |
filter: drop-shadow(0 8px 24px var(--brand-glow));
|
| 1080 |
}
|
|
|
|
| 2120 |
}
|
| 2121 |
|
| 2122 |
.hero-title { font-size: 22px; }
|
| 2123 |
+
.hero-bot-icon { width: 110px; height: 110px; }
|
| 2124 |
|
| 2125 |
.hero-welcome {
|
| 2126 |
justify-content: center;
|