Spaces:
Sleeping
Sleeping
minor improvements; fixes.. x2
Browse files- static/app.js +116 -29
- static/index.html +23 -0
- static/style.css +148 -19
static/app.js
CHANGED
|
@@ -474,12 +474,14 @@ if (overlaySettingsBtn) overlaySettingsBtn.addEventListener('click', () => {
|
|
| 474 |
|
| 475 |
|
| 476 |
if (overlayLogoutBtn) overlayLogoutBtn.addEventListener('click', () => {
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
|
|
|
|
|
|
| 483 |
});
|
| 484 |
|
| 485 |
function _renderOverlayHistory() {
|
|
@@ -498,46 +500,71 @@ function _renderOverlayHistory() {
|
|
| 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 |
-
<
|
| 502 |
-
|
| 503 |
-
|
|
|
|
|
|
|
| 504 |
</button>
|
| 505 |
-
<button class="overlay-
|
| 506 |
-
<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 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
|
|
|
|
|
|
| 522 |
});
|
|
|
|
| 523 |
item.querySelector('[data-action="delete"]').addEventListener('click', (e) => {
|
| 524 |
e.stopPropagation();
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
|
|
|
|
|
|
|
|
|
| 536 |
});
|
| 537 |
overlayHistoryList.appendChild(item);
|
| 538 |
});
|
| 539 |
}
|
| 540 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 541 |
|
| 542 |
/* ── Create Welcome Screen (Dynamic) ───────────────────────── */
|
| 543 |
|
|
@@ -793,7 +820,7 @@ if (userProfileBtn) userProfileBtn.addEventListener('click', (e) => { e.stopProp
|
|
| 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', () => {
|
| 797 |
|
| 798 |
document.querySelectorAll('.settings-nav-btn').forEach(btn => {
|
| 799 |
btn.addEventListener('click', () => {
|
|
@@ -1381,3 +1408,63 @@ function _closeAllCustomSelects() {
|
|
| 1381 |
document.querySelectorAll('.custom-select-btn.open').forEach(b => b.classList.remove('open'));
|
| 1382 |
}
|
| 1383 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 474 |
|
| 475 |
|
| 476 |
if (overlayLogoutBtn) overlayLogoutBtn.addEventListener('click', () => {
|
| 477 |
+
pwaConfirm('Do you want to log out?').then(yes => {
|
| 478 |
+
if (yes) {
|
| 479 |
+
localStorage.removeItem('stemcopilot_user');
|
| 480 |
+
localStorage.removeItem('stemcopilot_username');
|
| 481 |
+
currentUser = null;
|
| 482 |
+
location.reload();
|
| 483 |
+
}
|
| 484 |
+
});
|
| 485 |
});
|
| 486 |
|
| 487 |
function _renderOverlayHistory() {
|
|
|
|
| 500 |
<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>
|
| 501 |
<span class="overlay-history-title">${escapeHtml(thread.title)}</span>
|
| 502 |
</div>
|
| 503 |
+
<button class="overlay-kebab-btn" title="Options">⋮</button>
|
| 504 |
+
<div class="overlay-ctx-menu">
|
| 505 |
+
<button class="overlay-ctx-option" data-action="rename">
|
| 506 |
+
<svg viewBox="0 0 24 24" 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>
|
| 507 |
+
Rename
|
| 508 |
</button>
|
| 509 |
+
<button class="overlay-ctx-option danger" data-action="delete">
|
| 510 |
+
<svg viewBox="0 0 24 24" 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>
|
| 511 |
+
Delete
|
| 512 |
</button>
|
| 513 |
</div>
|
| 514 |
`;
|
| 515 |
+
// Load thread on main area click
|
| 516 |
item.querySelector('.overlay-history-main').addEventListener('click', () => {
|
| 517 |
_closeAllOverlays();
|
| 518 |
loadThread(thread.id);
|
| 519 |
});
|
| 520 |
+
// Kebab toggle
|
| 521 |
+
const kebab = item.querySelector('.overlay-kebab-btn');
|
| 522 |
+
const ctx = item.querySelector('.overlay-ctx-menu');
|
| 523 |
+
kebab.addEventListener('click', (e) => {
|
| 524 |
+
e.stopPropagation();
|
| 525 |
+
const wasOpen = ctx.classList.contains('show');
|
| 526 |
+
_closeAllCtxMenus();
|
| 527 |
+
if (!wasOpen) ctx.classList.add('show');
|
| 528 |
+
});
|
| 529 |
+
// Rename
|
| 530 |
item.querySelector('[data-action="rename"]').addEventListener('click', (e) => {
|
| 531 |
e.stopPropagation();
|
| 532 |
+
ctx.classList.remove('show');
|
| 533 |
+
pwaPrompt('Rename chat:', thread.title).then(newTitle => {
|
| 534 |
+
if (newTitle && newTitle.trim()) {
|
| 535 |
+
fetch('/rename', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ thread_id: thread.id, title: newTitle.trim() }) })
|
| 536 |
+
.then(() => { thread.title = newTitle.trim(); _renderOverlayHistory(); renderHistory(); })
|
| 537 |
+
.catch(() => showToast('Failed to rename.', 'error'));
|
| 538 |
+
}
|
| 539 |
+
});
|
| 540 |
});
|
| 541 |
+
// Delete
|
| 542 |
item.querySelector('[data-action="delete"]').addEventListener('click', (e) => {
|
| 543 |
e.stopPropagation();
|
| 544 |
+
ctx.classList.remove('show');
|
| 545 |
+
pwaConfirm('Delete this chat?').then(yes => {
|
| 546 |
+
if (yes) {
|
| 547 |
+
fetch('/thread/' + thread.id, { method: 'DELETE' })
|
| 548 |
+
.then(() => {
|
| 549 |
+
const idx = threads.findIndex(t => t.id === thread.id);
|
| 550 |
+
if (idx !== -1) threads.splice(idx, 1);
|
| 551 |
+
if (thread.id === currentThreadId) startNewChat();
|
| 552 |
+
_renderOverlayHistory();
|
| 553 |
+
renderHistory();
|
| 554 |
+
})
|
| 555 |
+
.catch(() => showToast('Failed to delete.', 'error'));
|
| 556 |
+
}
|
| 557 |
+
});
|
| 558 |
});
|
| 559 |
overlayHistoryList.appendChild(item);
|
| 560 |
});
|
| 561 |
}
|
| 562 |
|
| 563 |
+
function _closeAllCtxMenus() {
|
| 564 |
+
document.querySelectorAll('.overlay-ctx-menu.show').forEach(m => m.classList.remove('show'));
|
| 565 |
+
}
|
| 566 |
+
document.addEventListener('click', () => _closeAllCtxMenus());
|
| 567 |
+
|
| 568 |
|
| 569 |
/* ── Create Welcome Screen (Dynamic) ───────────────────────── */
|
| 570 |
|
|
|
|
| 820 |
if (openSettingsBtn) openSettingsBtn.addEventListener('click', () => { userMenu.classList.remove('show'); settingsOverlay.classList.add('show'); });
|
| 821 |
if (settingsCloseBtn) settingsCloseBtn.addEventListener('click', () => settingsOverlay.classList.remove('show'));
|
| 822 |
if (settingsOverlay) settingsOverlay.addEventListener('click', (e) => { if (e.target === settingsOverlay) settingsOverlay.classList.remove('show'); });
|
| 823 |
+
if (logoutBtn) logoutBtn.addEventListener('click', () => { pwaConfirm('Do you want to log out?').then(yes => { if (yes) { localStorage.removeItem('stemcopilot_user'); localStorage.removeItem('stemcopilot_username'); currentUser = null; location.reload(); } }); });
|
| 824 |
|
| 825 |
document.querySelectorAll('.settings-nav-btn').forEach(btn => {
|
| 826 |
btn.addEventListener('click', () => {
|
|
|
|
| 1408 |
document.querySelectorAll('.custom-select-btn.open').forEach(b => b.classList.remove('open'));
|
| 1409 |
}
|
| 1410 |
|
| 1411 |
+
|
| 1412 |
+
/* ── PWA Modal Utilities ───────────────────────────────────── */
|
| 1413 |
+
|
| 1414 |
+
function pwaConfirm(message) {
|
| 1415 |
+
return new Promise(resolve => {
|
| 1416 |
+
const overlay = document.getElementById('pwaConfirmOverlay');
|
| 1417 |
+
const msgEl = document.getElementById('pwaConfirmMessage');
|
| 1418 |
+
const okBtn = document.getElementById('pwaConfirmOk');
|
| 1419 |
+
const cancelBtn = document.getElementById('pwaConfirmCancel');
|
| 1420 |
+
msgEl.textContent = message;
|
| 1421 |
+
overlay.classList.add('show');
|
| 1422 |
+
|
| 1423 |
+
function cleanup(result) {
|
| 1424 |
+
overlay.classList.remove('show');
|
| 1425 |
+
okBtn.removeEventListener('click', onOk);
|
| 1426 |
+
cancelBtn.removeEventListener('click', onCancel);
|
| 1427 |
+
overlay.removeEventListener('click', onBg);
|
| 1428 |
+
resolve(result);
|
| 1429 |
+
}
|
| 1430 |
+
function onOk() { cleanup(true); }
|
| 1431 |
+
function onCancel() { cleanup(false); }
|
| 1432 |
+
function onBg(e) { if (e.target === overlay) cleanup(false); }
|
| 1433 |
+
|
| 1434 |
+
okBtn.addEventListener('click', onOk);
|
| 1435 |
+
cancelBtn.addEventListener('click', onCancel);
|
| 1436 |
+
overlay.addEventListener('click', onBg);
|
| 1437 |
+
});
|
| 1438 |
+
}
|
| 1439 |
+
|
| 1440 |
+
function pwaPrompt(message, defaultValue) {
|
| 1441 |
+
return new Promise(resolve => {
|
| 1442 |
+
const overlay = document.getElementById('pwaPromptOverlay');
|
| 1443 |
+
const msgEl = document.getElementById('pwaPromptMessage');
|
| 1444 |
+
const input = document.getElementById('pwaPromptInput');
|
| 1445 |
+
const okBtn = document.getElementById('pwaPromptOk');
|
| 1446 |
+
const cancelBtn = document.getElementById('pwaPromptCancel');
|
| 1447 |
+
msgEl.textContent = message;
|
| 1448 |
+
input.value = defaultValue || '';
|
| 1449 |
+
overlay.classList.add('show');
|
| 1450 |
+
setTimeout(() => input.focus(), 50);
|
| 1451 |
+
|
| 1452 |
+
function cleanup(result) {
|
| 1453 |
+
overlay.classList.remove('show');
|
| 1454 |
+
okBtn.removeEventListener('click', onOk);
|
| 1455 |
+
cancelBtn.removeEventListener('click', onCancel);
|
| 1456 |
+
overlay.removeEventListener('click', onBg);
|
| 1457 |
+
input.removeEventListener('keydown', onKey);
|
| 1458 |
+
resolve(result);
|
| 1459 |
+
}
|
| 1460 |
+
function onOk() { cleanup(input.value); }
|
| 1461 |
+
function onCancel() { cleanup(null); }
|
| 1462 |
+
function onBg(e) { if (e.target === overlay) cleanup(null); }
|
| 1463 |
+
function onKey(e) { if (e.key === 'Enter') { e.preventDefault(); cleanup(input.value); } }
|
| 1464 |
+
|
| 1465 |
+
okBtn.addEventListener('click', onOk);
|
| 1466 |
+
cancelBtn.addEventListener('click', onCancel);
|
| 1467 |
+
overlay.addEventListener('click', onBg);
|
| 1468 |
+
input.addEventListener('keydown', onKey);
|
| 1469 |
+
});
|
| 1470 |
+
}
|
static/index.html
CHANGED
|
@@ -562,6 +562,29 @@
|
|
| 562 |
</div><!-- /settings-modal -->
|
| 563 |
</div><!-- /settingsOverlay -->
|
| 564 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 565 |
<!-- Dependencies -->
|
| 566 |
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js"></script>
|
| 567 |
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js"></script>
|
|
|
|
| 562 |
</div><!-- /settings-modal -->
|
| 563 |
</div><!-- /settingsOverlay -->
|
| 564 |
|
| 565 |
+
<!-- ==================== PWA CONFIRM MODAL ==================== -->
|
| 566 |
+
<div class="pwa-modal-overlay" id="pwaConfirmOverlay">
|
| 567 |
+
<div class="pwa-modal">
|
| 568 |
+
<p class="pwa-modal-message" id="pwaConfirmMessage">Are you sure?</p>
|
| 569 |
+
<div class="pwa-modal-actions">
|
| 570 |
+
<button class="pwa-modal-btn pwa-modal-cancel" id="pwaConfirmCancel">No</button>
|
| 571 |
+
<button class="pwa-modal-btn pwa-modal-ok" id="pwaConfirmOk">Yes</button>
|
| 572 |
+
</div>
|
| 573 |
+
</div>
|
| 574 |
+
</div>
|
| 575 |
+
|
| 576 |
+
<!-- ==================== PWA PROMPT MODAL ==================== -->
|
| 577 |
+
<div class="pwa-modal-overlay" id="pwaPromptOverlay">
|
| 578 |
+
<div class="pwa-modal">
|
| 579 |
+
<p class="pwa-modal-message" id="pwaPromptMessage">Enter value:</p>
|
| 580 |
+
<input type="text" class="pwa-modal-input" id="pwaPromptInput" autocomplete="off" spellcheck="false">
|
| 581 |
+
<div class="pwa-modal-actions">
|
| 582 |
+
<button class="pwa-modal-btn pwa-modal-cancel" id="pwaPromptCancel">Cancel</button>
|
| 583 |
+
<button class="pwa-modal-btn pwa-modal-ok" id="pwaPromptOk">Save</button>
|
| 584 |
+
</div>
|
| 585 |
+
</div>
|
| 586 |
+
</div>
|
| 587 |
+
|
| 588 |
<!-- Dependencies -->
|
| 589 |
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js"></script>
|
| 590 |
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js"></script>
|
static/style.css
CHANGED
|
@@ -610,6 +610,7 @@ textarea, input, select {
|
|
| 610 |
width: 100%;
|
| 611 |
text-align: left;
|
| 612 |
font-family: inherit;
|
|
|
|
| 613 |
}
|
| 614 |
|
| 615 |
.overlay-history-main {
|
|
@@ -621,38 +622,80 @@ textarea, input, select {
|
|
| 621 |
cursor: pointer;
|
| 622 |
}
|
| 623 |
|
| 624 |
-
|
| 625 |
-
|
| 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-
|
| 648 |
background: var(--bg-hover);
|
| 649 |
color: var(--text-primary);
|
| 650 |
}
|
| 651 |
|
| 652 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 653 |
color: #ff4a4a;
|
| 654 |
}
|
| 655 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 656 |
.overlay-history-item:hover,
|
| 657 |
.overlay-history-item:active {
|
| 658 |
background: var(--bg-hover);
|
|
@@ -660,14 +703,10 @@ textarea, input, select {
|
|
| 660 |
}
|
| 661 |
|
| 662 |
.overlay-history-item.active {
|
| 663 |
-
background:
|
| 664 |
color: var(--text-primary);
|
| 665 |
}
|
| 666 |
|
| 667 |
-
[data-theme="light"] .overlay-history-item.active {
|
| 668 |
-
background: rgba(44, 40, 114, 0.08);
|
| 669 |
-
}
|
| 670 |
-
|
| 671 |
.overlay-history-item svg {
|
| 672 |
flex-shrink: 0;
|
| 673 |
opacity: 0.4;
|
|
@@ -1995,6 +2034,96 @@ textarea, input, select {
|
|
| 1995 |
.custom-select-option.active { background: var(--brand-glow); color: var(--brand); font-weight: 600; }
|
| 1996 |
|
| 1997 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1998 |
/* ============================================================
|
| 1999 |
SIDEBAR OVERLAY (mobile edge-swipe)
|
| 2000 |
============================================================ */
|
|
|
|
| 610 |
width: 100%;
|
| 611 |
text-align: left;
|
| 612 |
font-family: inherit;
|
| 613 |
+
position: relative;
|
| 614 |
}
|
| 615 |
|
| 616 |
.overlay-history-main {
|
|
|
|
| 622 |
cursor: pointer;
|
| 623 |
}
|
| 624 |
|
| 625 |
+
/* Kebab ⋮ menu trigger — always visible */
|
| 626 |
+
.overlay-kebab-btn {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 627 |
background: none;
|
| 628 |
border: none;
|
| 629 |
color: var(--text-muted);
|
| 630 |
cursor: pointer;
|
| 631 |
+
padding: 4px 6px;
|
| 632 |
border-radius: 6px;
|
| 633 |
+
font-size: 18px;
|
| 634 |
+
line-height: 1;
|
| 635 |
+
flex-shrink: 0;
|
| 636 |
display: flex;
|
| 637 |
align-items: center;
|
| 638 |
justify-content: center;
|
| 639 |
transition: background 0.2s, color 0.2s;
|
| 640 |
}
|
| 641 |
|
| 642 |
+
.overlay-kebab-btn:hover {
|
| 643 |
background: var(--bg-hover);
|
| 644 |
color: var(--text-primary);
|
| 645 |
}
|
| 646 |
|
| 647 |
+
/* Context menu that appears below the kebab */
|
| 648 |
+
.overlay-ctx-menu {
|
| 649 |
+
display: none;
|
| 650 |
+
position: absolute;
|
| 651 |
+
right: 8px;
|
| 652 |
+
top: calc(100% + 2px);
|
| 653 |
+
background: var(--bg-elevated);
|
| 654 |
+
border: 1px solid var(--border-color);
|
| 655 |
+
border-radius: 10px;
|
| 656 |
+
box-shadow: 0 8px 24px rgba(0,0,0,0.35);
|
| 657 |
+
z-index: 210;
|
| 658 |
+
min-width: 140px;
|
| 659 |
+
overflow: hidden;
|
| 660 |
+
animation: fadeIn 0.12s ease-out;
|
| 661 |
+
}
|
| 662 |
+
|
| 663 |
+
.overlay-ctx-menu.show { display: block; }
|
| 664 |
+
|
| 665 |
+
.overlay-ctx-option {
|
| 666 |
+
display: flex;
|
| 667 |
+
align-items: center;
|
| 668 |
+
gap: 10px;
|
| 669 |
+
padding: 11px 16px;
|
| 670 |
+
font-size: 14px;
|
| 671 |
+
color: var(--text-secondary);
|
| 672 |
+
cursor: pointer;
|
| 673 |
+
transition: background 0.15s, color 0.15s;
|
| 674 |
+
border: none;
|
| 675 |
+
background: none;
|
| 676 |
+
width: 100%;
|
| 677 |
+
font-family: inherit;
|
| 678 |
+
text-align: left;
|
| 679 |
+
}
|
| 680 |
+
|
| 681 |
+
.overlay-ctx-option:hover {
|
| 682 |
+
background: var(--bg-hover);
|
| 683 |
+
color: var(--text-primary);
|
| 684 |
+
}
|
| 685 |
+
|
| 686 |
+
.overlay-ctx-option.danger:hover {
|
| 687 |
color: #ff4a4a;
|
| 688 |
}
|
| 689 |
|
| 690 |
+
.overlay-ctx-option svg {
|
| 691 |
+
width: 14px;
|
| 692 |
+
height: 14px;
|
| 693 |
+
stroke: currentColor;
|
| 694 |
+
fill: none;
|
| 695 |
+
stroke-width: 2;
|
| 696 |
+
flex-shrink: 0;
|
| 697 |
+
}
|
| 698 |
+
|
| 699 |
.overlay-history-item:hover,
|
| 700 |
.overlay-history-item:active {
|
| 701 |
background: var(--bg-hover);
|
|
|
|
| 703 |
}
|
| 704 |
|
| 705 |
.overlay-history-item.active {
|
| 706 |
+
background: var(--bg-hover);
|
| 707 |
color: var(--text-primary);
|
| 708 |
}
|
| 709 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 710 |
.overlay-history-item svg {
|
| 711 |
flex-shrink: 0;
|
| 712 |
opacity: 0.4;
|
|
|
|
| 2034 |
.custom-select-option.active { background: var(--brand-glow); color: var(--brand); font-weight: 600; }
|
| 2035 |
|
| 2036 |
|
| 2037 |
+
/* ============================================================
|
| 2038 |
+
PWA MODAL DIALOGS (replaces Chrome confirm/prompt)
|
| 2039 |
+
============================================================ */
|
| 2040 |
+
|
| 2041 |
+
.pwa-modal-overlay {
|
| 2042 |
+
display: none;
|
| 2043 |
+
position: fixed;
|
| 2044 |
+
inset: 0;
|
| 2045 |
+
background: rgba(0, 0, 0, 0.55);
|
| 2046 |
+
z-index: 9999;
|
| 2047 |
+
justify-content: center;
|
| 2048 |
+
align-items: center;
|
| 2049 |
+
animation: fadeIn 0.15s ease-out;
|
| 2050 |
+
}
|
| 2051 |
+
|
| 2052 |
+
.pwa-modal-overlay.show {
|
| 2053 |
+
display: flex;
|
| 2054 |
+
}
|
| 2055 |
+
|
| 2056 |
+
.pwa-modal {
|
| 2057 |
+
background: var(--bg-elevated);
|
| 2058 |
+
border: 1px solid var(--border-color);
|
| 2059 |
+
border-radius: 16px;
|
| 2060 |
+
padding: 24px;
|
| 2061 |
+
width: 90vw;
|
| 2062 |
+
max-width: 340px;
|
| 2063 |
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
|
| 2064 |
+
animation: fadeIn 0.2s ease-out;
|
| 2065 |
+
}
|
| 2066 |
+
|
| 2067 |
+
.pwa-modal-message {
|
| 2068 |
+
font-size: 15px;
|
| 2069 |
+
font-weight: 500;
|
| 2070 |
+
color: var(--text-primary);
|
| 2071 |
+
margin: 0 0 18px;
|
| 2072 |
+
line-height: 1.5;
|
| 2073 |
+
text-align: center;
|
| 2074 |
+
}
|
| 2075 |
+
|
| 2076 |
+
.pwa-modal-input {
|
| 2077 |
+
width: 100%;
|
| 2078 |
+
padding: 11px 14px;
|
| 2079 |
+
background: var(--bg-input);
|
| 2080 |
+
border: 1px solid var(--border-color);
|
| 2081 |
+
border-radius: 8px;
|
| 2082 |
+
color: var(--text-primary);
|
| 2083 |
+
font-family: inherit;
|
| 2084 |
+
font-size: 14px;
|
| 2085 |
+
margin-bottom: 18px;
|
| 2086 |
+
outline: none;
|
| 2087 |
+
transition: border-color 0.2s;
|
| 2088 |
+
box-sizing: border-box;
|
| 2089 |
+
}
|
| 2090 |
+
|
| 2091 |
+
.pwa-modal-input:focus {
|
| 2092 |
+
border-color: var(--brand);
|
| 2093 |
+
}
|
| 2094 |
+
|
| 2095 |
+
.pwa-modal-actions {
|
| 2096 |
+
display: flex;
|
| 2097 |
+
gap: 10px;
|
| 2098 |
+
}
|
| 2099 |
+
|
| 2100 |
+
.pwa-modal-btn {
|
| 2101 |
+
flex: 1;
|
| 2102 |
+
padding: 11px 0;
|
| 2103 |
+
border: none;
|
| 2104 |
+
border-radius: 10px;
|
| 2105 |
+
font-family: inherit;
|
| 2106 |
+
font-size: 14px;
|
| 2107 |
+
font-weight: 600;
|
| 2108 |
+
cursor: pointer;
|
| 2109 |
+
transition: background 0.2s, opacity 0.2s;
|
| 2110 |
+
}
|
| 2111 |
+
|
| 2112 |
+
.pwa-modal-cancel {
|
| 2113 |
+
background: var(--bg-hover);
|
| 2114 |
+
color: var(--text-secondary);
|
| 2115 |
+
}
|
| 2116 |
+
|
| 2117 |
+
.pwa-modal-cancel:hover { opacity: 0.8; }
|
| 2118 |
+
|
| 2119 |
+
.pwa-modal-ok {
|
| 2120 |
+
background: var(--brand);
|
| 2121 |
+
color: #fff;
|
| 2122 |
+
}
|
| 2123 |
+
|
| 2124 |
+
.pwa-modal-ok:hover { background: var(--brand-hover); }
|
| 2125 |
+
|
| 2126 |
+
|
| 2127 |
/* ============================================================
|
| 2128 |
SIDEBAR OVERLAY (mobile edge-swipe)
|
| 2129 |
============================================================ */
|