incognitolm commited on
Commit
83057f7
Β·
1 Parent(s): 92bc1dd

Mobile UI

Browse files
public/css/chat.css CHANGED
@@ -360,51 +360,60 @@ span[data-color="pink"] { color: #f472b6; }
360
  0%, 100% { opacity: 1; }
361
  50% { opacity: 0.75; }
362
  }
 
363
  /* ══════════════════════════════════════════════════════════════════════════
364
- MOBILE CHAT β€” Proper scroll containment, no overflow leakage
365
  ══════════════════════════════════════════════════════════════════════════ */
366
 
367
  @media (max-width: 768px) {
368
 
369
- /* #main fills available height; overflow must not escape */
370
  #main {
371
  overflow: hidden;
 
 
372
  }
373
 
374
- /* Chat view: the ONLY vertical scrollbar in the page */
375
  .chat-view {
 
 
376
  overflow-y: auto;
377
  overflow-x: hidden;
378
  -webkit-overflow-scrolling: touch;
379
- overscroll-behavior: contain;
380
  }
381
 
382
- /* Welcome view: no scroll on the outer container */
383
  .welcome-view {
 
 
384
  overflow: hidden;
 
 
 
 
385
  }
386
 
387
- /* Messages container: no extra padding */
388
  .chat-messages {
389
- padding: 0 16px;
390
  max-width: 100%;
391
  }
392
 
393
- /* User bubbles: slightly wider on narrow screens */
394
  .msg-user {
395
- max-width: 90%;
396
  font-size: 14px;
397
  padding: 9px 14px;
398
  }
399
 
400
- /* Assistant messages: full width */
401
  .msg-assistant {
402
  max-width: 100%;
403
  font-size: 14px;
404
- padding: 4px 0 28px;
405
  }
406
 
407
- /* Code blocks: horizontal scroll only (already set, reinforce) */
408
  .code-block {
409
  overflow: hidden;
410
  }
 
360
  0%, 100% { opacity: 1; }
361
  50% { opacity: 0.75; }
362
  }
363
+
364
  /* ══════════════════════════════════════════════════════════════════════════
365
+ MOBILE CHAT β€” Proper scroll containment, centered welcome
366
  ══════════════════════════════════════════════════════════════════════════ */
367
 
368
  @media (max-width: 768px) {
369
 
370
+ /* Main: full height, no overflow */
371
  #main {
372
  overflow: hidden;
373
+ display: flex;
374
+ flex-direction: column;
375
  }
376
 
377
+ /* Chat view: the ONE vertical scrollbar on the page */
378
  .chat-view {
379
+ flex: 1;
380
+ min-height: 0;
381
  overflow-y: auto;
382
  overflow-x: hidden;
383
  -webkit-overflow-scrolling: touch;
384
+ overscroll-behavior-y: contain;
385
  }
386
 
387
+ /* Welcome view: centered, no scroll */
388
  .welcome-view {
389
+ flex: 1;
390
+ min-height: 0;
391
  overflow: hidden;
392
+ display: flex;
393
+ flex-direction: column;
394
+ align-items: center;
395
+ justify-content: center;
396
  }
397
 
398
+ /* Messages: no extra horizontal padding */
399
  .chat-messages {
400
+ padding: 0 14px;
401
  max-width: 100%;
402
  }
403
 
404
+ /* Bubbles */
405
  .msg-user {
406
+ max-width: 88%;
407
  font-size: 14px;
408
  padding: 9px 14px;
409
  }
410
 
 
411
  .msg-assistant {
412
  max-width: 100%;
413
  font-size: 14px;
 
414
  }
415
 
416
+ /* Code blocks: horizontal scroll only */
417
  .code-block {
418
  overflow: hidden;
419
  }
public/css/input.css CHANGED
@@ -253,44 +253,108 @@
253
  padding: 6px;
254
  }
255
  }
 
256
  /* ══════════════════════════════════════════════════════════════════════════
257
- MOBILE INPUT β€” No extra scrollbars, TOS repositioned
258
  ══════════════════════════════════════════════════════════════════════════ */
259
 
260
  @media (max-width: 768px) {
261
 
262
- /* TOS banner: fixed just above the bottom nav bar */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  #tos-banner {
264
- bottom: 60px; /* height of collapsed mobile sidebar */
 
 
 
265
  font-size: 11px;
266
- padding: 6px 12px;
267
- width: 100%;
268
- left: 0;
269
- transform: none;
270
- background: var(--bg);
271
  border-top: 1px solid var(--border);
 
 
 
272
  }
273
 
274
- /* Bottom input bar: sits above TOS + bottom nav */
275
- .bottom-input-bar {
276
- padding: 0 12px 4px !important;
277
- /* JS will set padding-bottom dynamically; this is a safe fallback */
278
  }
279
 
280
- /* Input wrap: slightly tighter padding */
281
- .bottom-input-wrap {
282
- padding: 6px 8px;
283
  }
284
 
285
- /* Welcome view: full height minus mobile chrome */
286
  .welcome-view {
287
- padding: 24px 16px !important;
288
- justify-content: flex-start;
289
- padding-top: 48px !important;
 
 
 
 
 
 
 
 
290
  }
291
 
292
  .welcome-input-wrap {
293
  width: 100%;
 
 
 
 
 
 
 
294
  }
295
 
296
  /* Prevent any horizontal overflow */
@@ -298,4 +362,11 @@
298
  .bottom-input-wrap {
299
  max-width: 100%;
300
  }
 
 
 
 
 
 
 
301
  }
 
253
  padding: 6px;
254
  }
255
  }
256
+
257
  /* ══════════════════════════════════════════════════════════════════════════
258
+ MOBILE INPUT β€” Top bar chrome, TOS at bottom, no overflow
259
  ══════════════════════════════════════════════════════════════════════════ */
260
 
261
  @media (max-width: 768px) {
262
 
263
+ /* ── Mobile top header bar (new chat + sidebar buttons) ── */
264
+ #mobile-top-bar {
265
+ display: flex;
266
+ align-items: center;
267
+ justify-content: space-between;
268
+ padding: 10px 16px;
269
+ background: var(--bg);
270
+ border-bottom: 1px solid var(--border);
271
+ position: fixed;
272
+ top: 0;
273
+ left: 0;
274
+ right: 0;
275
+ height: 52px;
276
+ z-index: 90;
277
+ transform: translateY(0);
278
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
279
+ flex-shrink: 0;
280
+ }
281
+
282
+ #mobile-top-bar.hidden-bar {
283
+ transform: translateY(-100%);
284
+ }
285
+
286
+ #mobile-top-bar .mobile-bar-title {
287
+ font-size: 15px;
288
+ font-weight: 600;
289
+ color: var(--text);
290
+ letter-spacing: -0.01em;
291
+ }
292
+
293
+ #mobile-top-bar .mobile-bar-btn {
294
+ display: flex;
295
+ align-items: center;
296
+ justify-content: center;
297
+ width: 36px;
298
+ height: 36px;
299
+ border-radius: var(--radius-md);
300
+ color: var(--text-dim);
301
+ transition: background var(--transition), color var(--transition);
302
+ }
303
+
304
+ #mobile-top-bar .mobile-bar-btn:hover,
305
+ #mobile-top-bar .mobile-bar-btn:active {
306
+ background: var(--bg-hover);
307
+ color: var(--text);
308
+ }
309
+
310
+ /* ── TOS banner: pinned to very bottom ── */
311
  #tos-banner {
312
+ bottom: 0 !important;
313
+ left: 0 !important;
314
+ transform: none !important;
315
+ width: 100% !important;
316
  font-size: 11px;
317
+ padding: 6px 14px;
 
 
 
 
318
  border-top: 1px solid var(--border);
319
+ background: var(--bg);
320
+ z-index: 80;
321
+ text-align: center;
322
  }
323
 
324
+ /* ── Main area: padded for top bar + TOS ── */
325
+ #main {
326
+ padding-top: 52px;
 
327
  }
328
 
329
+ /* ── Bottom input bar: sits just above TOS ── */
330
+ .bottom-input-bar {
331
+ padding: 0 12px 40px !important;
332
  }
333
 
334
+ /* ── Welcome view: truly vertically centered ── */
335
  .welcome-view {
336
+ padding-top: 0 !important;
337
+ padding-bottom: 40px !important;
338
+ justify-content: center !important;
339
+ align-items: center !important;
340
+ height: 100% !important;
341
+ gap: 20px !important;
342
+ }
343
+
344
+ .welcome-title {
345
+ text-align: center;
346
+ font-size: clamp(22px, 6vw, 36px) !important;
347
  }
348
 
349
  .welcome-input-wrap {
350
  width: 100%;
351
+ padding: 0 4px;
352
+ }
353
+
354
+ /* Input wrap: tighter on mobile */
355
+ .bottom-input-wrap,
356
+ .center-input-container {
357
+ padding: 6px 8px;
358
  }
359
 
360
  /* Prevent any horizontal overflow */
 
362
  .bottom-input-wrap {
363
  max-width: 100%;
364
  }
365
+ }
366
+
367
+ /* Desktop: never show the mobile top bar */
368
+ @media (min-width: 769px) {
369
+ #mobile-top-bar {
370
+ display: none !important;
371
+ }
372
  }
public/css/sidebar.css CHANGED
@@ -282,132 +282,116 @@
282
  .context-item svg { flex-shrink: 0; color: var(--text-dim); }
283
  .context-item.danger svg { color: #f07070; }
284
  /* ══════════════════════════════════════════════════════════════════════════
285
- MOBILE SIDEBAR β€” Bottom navigation drawer pattern
 
286
  ══════════════════════════════════════════════════════════════════════════ */
287
 
288
  @media (max-width: 768px) {
289
 
290
- /* ── Root layout: full-height columns β†’ full-height rows ── */
291
  #app {
292
  flex-direction: column !important;
293
  height: 100dvh;
294
  overflow: hidden;
295
  }
296
 
297
- /* ── Sidebar becomes a bottom drawer ── */
298
  .sidebar {
299
  position: fixed !important;
300
- bottom: 0;
301
- left: 0;
302
- right: 0;
303
- top: auto !important;
304
- width: 100% !important;
305
- height: auto !important;
306
- max-height: 72px; /* collapsed = icon strip */
307
  border-right: none !important;
308
- border-top: 1px solid var(--sidebar-border) !important;
309
- flex-direction: row;
310
  z-index: var(--z-sidebar);
311
  overflow: hidden;
312
- transition: max-height var(--transition-slow);
 
313
  }
314
 
315
- /* Collapsed mobile: icon strip at bottom */
316
  .sidebar.collapsed {
317
- max-height: 60px;
318
  }
319
 
320
- /* Expanded mobile: slide up as drawer */
321
  .sidebar.expanded {
322
- max-height: 75dvh;
323
- flex-direction: column;
324
  }
325
 
326
- /* Top icon strip becomes a horizontal nav row */
327
  .sidebar-top {
328
  flex-direction: row !important;
329
- padding: 10px 14px !important;
330
- gap: 6px !important;
331
- border-bottom: none !important;
332
- border-bottom: 1px solid var(--sidebar-border);
333
  flex-shrink: 0;
334
  }
335
 
336
- /* Session list: shown only when expanded, scrollable */
337
  .sidebar-sessions {
338
- opacity: 0;
339
- pointer-events: none;
340
  overflow-y: auto;
341
  flex: 1;
 
342
  }
343
 
344
- .sidebar.expanded .sidebar-sessions {
345
- opacity: 1;
346
- pointer-events: auto;
347
- }
348
-
349
- /* Bottom account section */
350
  .sidebar-bottom {
351
  flex-shrink: 0;
352
  border-top: 1px solid var(--sidebar-border);
 
353
  }
354
 
355
- .sidebar.expanded .sidebar-bottom {
356
- display: block;
357
- }
358
-
359
- .sidebar.collapsed .sidebar-bottom {
360
- display: none;
361
- }
362
-
363
- /* Account section always horizontal on mobile */
364
  .account-section {
365
  flex-direction: row !important;
366
  align-items: center;
367
  }
368
 
369
- /* Sign-in button always visible on mobile when expanded */
370
- .sidebar.expanded .btn-signin {
371
- display: block;
372
- opacity: 1;
373
- pointer-events: auto;
374
  }
375
 
376
- /* Collapsed: hide sign-in icon button in bottom bar (space is tight) */
377
- .sidebar.collapsed .btn-signin-icon {
378
- display: none;
379
  }
380
 
381
- /* User info visible when expanded */
382
- .sidebar.expanded .user-info {
383
- opacity: 1;
384
- pointer-events: auto;
385
  }
386
 
387
- /* User profile button: full width on mobile */
388
- .sidebar.expanded .user-profile-btn {
389
  width: auto !important;
390
- justify-content: flex-start;
391
  border-radius: var(--radius-md) !important;
 
392
  }
393
 
394
- /* ── Main content: fills remaining space above bottom nav ── */
395
  #main {
396
- height: calc(100dvh - 60px) !important;
397
- padding-bottom: 0 !important;
398
  overflow: hidden;
399
  }
400
 
401
- /* Collapse sidebar when tapping outside (backdrop) */
402
  .sidebar-backdrop {
403
  display: none;
404
  position: fixed;
405
  inset: 0;
406
- bottom: 75dvh; /* above the drawer */
407
- background: rgba(0,0,0,0.45);
408
  z-index: calc(var(--z-sidebar) - 1);
 
 
409
  }
410
- .sidebar.expanded ~ .sidebar-backdrop,
411
  .sidebar-backdrop.visible {
412
  display: block;
413
  }
 
282
  .context-item svg { flex-shrink: 0; color: var(--text-dim); }
283
  .context-item.danger svg { color: #f07070; }
284
  /* ══════════════════════════════════════════════════════════════════════════
285
+ /* ══════════════════════════════════════════════════════════════════════════
286
+ MOBILE SIDEBAR β€” Top header bar + full-screen left drawer
287
  ══════════════════════════════════════════════════════════════════════════ */
288
 
289
  @media (max-width: 768px) {
290
 
291
+ /* ── Root layout ── */
292
  #app {
293
  flex-direction: column !important;
294
  height: 100dvh;
295
  overflow: hidden;
296
  }
297
 
298
+ /* ── Sidebar: hidden off-screen left, slides in as full-screen overlay ── */
299
  .sidebar {
300
  position: fixed !important;
301
+ top: 0 !important;
302
+ left: 0 !important;
303
+ bottom: 0 !important;
304
+ width: 100vw !important;
305
+ height: 100dvh !important;
306
+ max-height: none !important;
 
307
  border-right: none !important;
308
+ border-top: none !important;
309
+ flex-direction: column !important;
310
  z-index: var(--z-sidebar);
311
  overflow: hidden;
312
+ transform: translateX(-100%);
313
+ transition: transform var(--transition-slow);
314
  }
315
 
 
316
  .sidebar.collapsed {
317
+ transform: translateX(-100%);
318
  }
319
 
 
320
  .sidebar.expanded {
321
+ transform: translateX(0);
 
322
  }
323
 
324
+ /* ── Sidebar inner top: header row ── */
325
  .sidebar-top {
326
  flex-direction: row !important;
327
+ padding: 14px 16px !important;
328
+ gap: 8px !important;
329
+ border-bottom: 1px solid var(--sidebar-border) !important;
 
330
  flex-shrink: 0;
331
  }
332
 
333
+ /* Session list: scrollable */
334
  .sidebar-sessions {
335
+ opacity: 1 !important;
336
+ pointer-events: auto !important;
337
  overflow-y: auto;
338
  flex: 1;
339
+ -webkit-overflow-scrolling: touch;
340
  }
341
 
342
+ /* Bottom account section: always show */
 
 
 
 
 
343
  .sidebar-bottom {
344
  flex-shrink: 0;
345
  border-top: 1px solid var(--sidebar-border);
346
+ display: block !important;
347
  }
348
 
 
 
 
 
 
 
 
 
 
349
  .account-section {
350
  flex-direction: row !important;
351
  align-items: center;
352
  }
353
 
354
+ .btn-signin {
355
+ display: block !important;
356
+ opacity: 1 !important;
357
+ pointer-events: auto !important;
 
358
  }
359
 
360
+ .btn-signin-icon {
361
+ display: none !important;
 
362
  }
363
 
364
+ .user-info {
365
+ opacity: 1 !important;
366
+ pointer-events: auto !important;
 
367
  }
368
 
369
+ .user-profile-btn {
370
+ flex: 1 !important;
371
  width: auto !important;
372
+ justify-content: flex-start !important;
373
  border-radius: var(--radius-md) !important;
374
+ gap: 9px !important;
375
  }
376
 
377
+ /* ── Main: full viewport ── */
378
  #main {
379
+ height: 100dvh !important;
380
+ width: 100% !important;
381
  overflow: hidden;
382
  }
383
 
384
+ /* ── Backdrop ── */
385
  .sidebar-backdrop {
386
  display: none;
387
  position: fixed;
388
  inset: 0;
389
+ background: rgba(0, 0, 0, 0.55);
 
390
  z-index: calc(var(--z-sidebar) - 1);
391
+ backdrop-filter: blur(2px);
392
+ -webkit-backdrop-filter: blur(2px);
393
  }
394
+
395
  .sidebar-backdrop.visible {
396
  display: block;
397
  }
public/index.html CHANGED
@@ -17,8 +17,15 @@
17
  html, body { overflow: hidden; height: 100%; max-height: 100dvh; }
18
  #app { display: flex; height: 100dvh; overflow: hidden; }
19
  @media (max-width: 768px) {
20
- #main { height: calc(100dvh - 60px); max-height: calc(100dvh - 60px); }
21
- .chat-view { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; }
 
 
 
 
 
 
 
22
  }
23
  </style>
24
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" />
@@ -40,6 +47,17 @@
40
  </div>
41
  </div>
42
 
 
 
 
 
 
 
 
 
 
 
 
43
  <div id="app">
44
  <!-- Sidebar -->
45
  <aside id="sidebar" class="sidebar collapsed">
 
17
  html, body { overflow: hidden; height: 100%; max-height: 100dvh; }
18
  #app { display: flex; height: 100dvh; overflow: hidden; }
19
  @media (max-width: 768px) {
20
+ #main {
21
+ height: 100dvh;
22
+ max-height: 100dvh;
23
+ display: flex;
24
+ flex-direction: column;
25
+ overflow: hidden;
26
+ }
27
+ /* Push content below the fixed top bar */
28
+ .chat-view, .welcome-view { padding-top: 0; }
29
  }
30
  </style>
31
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" />
 
47
  </div>
48
  </div>
49
 
50
+ <!-- Mobile top bar (hidden on desktop via CSS) -->
51
+ <div id="mobile-top-bar">
52
+ <button id="mobile-sidebar-btn" class="mobile-bar-btn" title="Menu">
53
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
54
+ </button>
55
+ <span class="mobile-bar-title">InferencePort AI</span>
56
+ <button id="mobile-newchat-btn" class="mobile-bar-btn" title="New Chat">
57
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
58
+ </button>
59
+ </div>
60
+
61
  <div id="app">
62
  <!-- Sidebar -->
63
  <aside id="sidebar" class="sidebar collapsed">
public/js/app.js CHANGED
@@ -17,13 +17,18 @@ import { showNotification, autoResize, escHtml } from './ui.js';
17
  if (!banner) return;
18
  const applyPadding = () => {
19
  const isMobile = window.innerWidth <= 768;
20
- // On mobile the sidebar is a bottom nav (60px) + TOS banner above it
21
- const mobileNavH = isMobile ? 60 : 0;
22
- const h = banner.offsetHeight || 36;
23
- const totalBottom = h + mobileNavH + 8;
24
- document.getElementById('bottom-input-bar')?.style.setProperty('padding-bottom', `${totalBottom}px`);
25
- const welcomeView = document.getElementById('welcome-view');
26
- if (welcomeView) welcomeView.style.paddingBottom = `${totalBottom + 16}px`;
 
 
 
 
 
27
  };
28
  requestAnimationFrame(() => { applyPadding(); });
29
  window.addEventListener('resize', applyPadding);
@@ -40,42 +45,83 @@ function toggleSidebar() { sidebar?.classList.contains('expanded') ? collapseSi
40
 
41
  toggleBtn?.addEventListener('click', toggleSidebar);
42
 
43
- // ── Mobile: backdrop to close sidebar drawer ──────────────────────────────
44
 
45
- (function setupMobileBackdrop() {
 
46
  const backdrop = document.createElement('div');
47
  backdrop.id = 'sidebar-backdrop';
48
  backdrop.className = 'sidebar-backdrop';
49
  document.body.appendChild(backdrop);
50
 
51
- function updateBackdrop() {
52
- if (window.innerWidth <= 768 && sidebar?.classList.contains('expanded')) {
53
- backdrop.classList.add('visible');
54
- } else {
55
- backdrop.classList.remove('visible');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  }
57
  }
58
 
59
- backdrop.addEventListener('click', () => { collapseSidebar(); updateBackdrop(); });
60
- toggleBtn?.addEventListener('click', () => { setTimeout(updateBackdrop, 50); });
61
- window.addEventListener('resize', () => {
62
- if (window.innerWidth > 768) backdrop.classList.remove('visible');
63
- });
64
 
65
- // Patch expand/collapse to also update backdrop
66
- const origExpand = expandSidebar, origCollapse = collapseSidebar;
67
- window._mobileUpdateBackdrop = updateBackdrop;
 
 
 
 
68
  })();
69
 
70
  // ── New chat β€” just show the welcome screen; don't create a session yet ───
71
 
72
  document.getElementById('new-chat-btn')?.addEventListener('click', () => {
73
  showWelcomeScreen();
74
- if (window.innerWidth < 768) collapseSidebar();
75
- // Clear the center input so the user starts fresh
76
  const ci = document.getElementById('center-input');
77
  if (ci) { ci.value = ''; autoResize(ci, 6); }
78
- // Clear chat messages so old chat doesn't show through
79
  const box = document.getElementById('chat-messages');
80
  if (box) box.innerHTML = '';
81
  });
@@ -100,7 +146,6 @@ onSessionChange((event, data) => {
100
  if (event === 'created') {
101
  setActiveSession(data.id);
102
  switchSession(data.id);
103
- if (window.innerWidth < 768) collapseSidebar();
104
  }
105
  });
106
 
 
17
  if (!banner) return;
18
  const applyPadding = () => {
19
  const isMobile = window.innerWidth <= 768;
20
+ if (isMobile) {
21
+ // On mobile: TOS is fixed at bottom, bottom-input-bar padding is set via CSS
22
+ // Just ensure welcome-view accounts for TOS height
23
+ const h = banner.offsetHeight || 36;
24
+ const welcomeView = document.getElementById('welcome-view');
25
+ if (welcomeView) welcomeView.style.paddingBottom = `${h + 8}px`;
26
+ } else {
27
+ const h = banner.offsetHeight || 42;
28
+ document.getElementById('bottom-input-bar')?.style.setProperty('padding-bottom', `${h + 8}px`);
29
+ const welcomeView = document.getElementById('welcome-view');
30
+ if (welcomeView) welcomeView.style.paddingBottom = `${h + 16}px`;
31
+ }
32
  };
33
  requestAnimationFrame(() => { applyPadding(); });
34
  window.addEventListener('resize', applyPadding);
 
45
 
46
  toggleBtn?.addEventListener('click', toggleSidebar);
47
 
48
+ // ── Mobile top bar + sidebar logic ───────────────────────────────────────
49
 
50
+ (function setupMobile() {
51
+ // Create backdrop
52
  const backdrop = document.createElement('div');
53
  backdrop.id = 'sidebar-backdrop';
54
  backdrop.className = 'sidebar-backdrop';
55
  document.body.appendChild(backdrop);
56
 
57
+ function openSidebar() { sidebar?.classList.remove('collapsed'); sidebar?.classList.add('expanded'); backdrop.classList.add('visible'); }
58
+ function closeSidebar() { sidebar?.classList.remove('expanded'); sidebar?.classList.add('collapsed'); backdrop.classList.remove('visible'); }
59
+
60
+ backdrop.addEventListener('click', closeSidebar);
61
+
62
+ // Wire mobile top bar buttons
63
+ document.getElementById('mobile-sidebar-btn')?.addEventListener('click', () => {
64
+ sidebar?.classList.contains('expanded') ? closeSidebar() : openSidebar();
65
+ });
66
+ document.getElementById('mobile-newchat-btn')?.addEventListener('click', () => {
67
+ showWelcomeScreen();
68
+ const ci = document.getElementById('center-input');
69
+ if (ci) { ci.value = ''; autoResize(ci, 6); }
70
+ const box = document.getElementById('chat-messages');
71
+ if (box) box.innerHTML = '';
72
+ });
73
+
74
+ // Auto-close drawer when a session is switched on mobile
75
+ onSessionChange((event) => {
76
+ if ((event === 'switched' || event === 'created') && window.innerWidth <= 768) {
77
+ closeSidebar();
78
+ }
79
+ });
80
+
81
+ // ── Hide/show top bar on scroll ─────────────────────────────────────────
82
+ let lastScrollY = 0;
83
+ let ticking = false;
84
+ const topBar = document.getElementById('mobile-top-bar');
85
+
86
+ function handleChatScroll(e) {
87
+ if (window.innerWidth > 768) return;
88
+ const el = e.target;
89
+ const currentY = el.scrollTop;
90
+ if (!ticking) {
91
+ requestAnimationFrame(() => {
92
+ if (currentY > lastScrollY && currentY > 60) {
93
+ // Scrolling down β€” hide bar
94
+ topBar?.classList.add('hidden-bar');
95
+ } else {
96
+ // Scrolling up β€” show bar
97
+ topBar?.classList.remove('hidden-bar');
98
+ }
99
+ lastScrollY = currentY <= 0 ? 0 : currentY;
100
+ ticking = false;
101
+ });
102
+ ticking = true;
103
  }
104
  }
105
 
106
+ // Attach to chat-view scroll
107
+ document.getElementById('chat-view')?.addEventListener('scroll', handleChatScroll, { passive: true });
 
 
 
108
 
109
+ // Always show top bar when on welcome screen
110
+ onSessionChange((event, data) => {
111
+ if (event === 'switched' && !data) {
112
+ topBar?.classList.remove('hidden-bar');
113
+ lastScrollY = 0;
114
+ }
115
+ });
116
  })();
117
 
118
  // ── New chat β€” just show the welcome screen; don't create a session yet ───
119
 
120
  document.getElementById('new-chat-btn')?.addEventListener('click', () => {
121
  showWelcomeScreen();
122
+ if (window.innerWidth < 768) { /* handled by setupMobile onSessionChange */ }
 
123
  const ci = document.getElementById('center-input');
124
  if (ci) { ci.value = ''; autoResize(ci, 6); }
 
125
  const box = document.getElementById('chat-messages');
126
  if (box) box.innerHTML = '';
127
  });
 
146
  if (event === 'created') {
147
  setActiveSession(data.id);
148
  switchSession(data.id);
 
149
  }
150
  });
151