Subh775 commited on
Commit
af7eda7
·
1 Parent(s): 9db75b8

minor improvements; fixes.. x2

Browse files
Files changed (3) hide show
  1. static/app.js +116 -29
  2. static/index.html +23 -0
  3. 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
- 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() {
@@ -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
- <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
  }
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', () => { 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', () => {
@@ -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">&#8942;</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
- .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);
@@ -660,14 +703,10 @@ textarea, input, select {
660
  }
661
 
662
  .overlay-history-item.active {
663
- background: rgba(236, 100, 43, 0.08);
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
  ============================================================ */