diff --git a/static/app.js b/static/app.js index 6d11106b4..262ecdb19 100644 --- a/static/app.js +++ b/static/app.js @@ -87,6 +87,37 @@ function initForegroundActivityHeartbeat() { } initForegroundActivityHeartbeat(); +function initRailHoverLabels() { + const labels = { + 'rail-search-btn': 'Search', + 'rail-new-session': 'New', + 'rail-delete-session': 'Delete', + 'rail-chats': 'Chat', + 'rail-documents': 'Docs', + 'rail-calendar': 'Calendar', + 'rail-compare': 'Compare', + 'rail-cookbook': 'Cookbook', + 'rail-research': 'Research', + 'rail-email': 'Email', + 'rail-gallery': 'Gallery', + 'rail-archive': 'Library', + 'rail-memory': 'Brain', + 'rail-notes': 'Notes', + 'rail-tasks': 'Tasks', + 'rail-theme': 'Theme', + 'rail-settings': 'Settings', + }; + document.querySelectorAll('#icon-rail .icon-rail-btn').forEach(btn => { + if (btn.querySelector('.rail-hover-label')) return; + const label = labels[btn.id] || btn.getAttribute('aria-label') || btn.getAttribute('title') || ''; + if (!label) return; + const span = document.createElement('span'); + span.className = 'rail-hover-label'; + span.textContent = String(label).replace(/\s*\([^)]*\)\s*/g, '').trim(); + btn.appendChild(span); + }); +} + // Redirect to login on 401 from any fetch const _origFetch = window.fetch; window.fetch = async function(...args) { @@ -3420,6 +3451,7 @@ function startOdysseusApp() { window.__odysseusAppStarted = true; // Set CSS variables document.documentElement.style.setProperty('--line-height', '20px'); + initRailHoverLabels(); // Smooth keyboard open/close on mobile — keep chat scrolled to bottom if (window.visualViewport && 'ontouchstart' in window) { diff --git a/static/style.css b/static/style.css index b81e692e4..2f27131c5 100644 --- a/static/style.css +++ b/static/style.css @@ -1120,11 +1120,9 @@ body.bg-pattern-sparkles { } .rail-new-chat svg { transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1); } .rail-new-chat:hover svg { transform: rotate(90deg); } - /* A "New" label slides out from the right of the + as it spins, so users - discover what the icon does without needing the tooltip. */ - .rail-new-chat { position: relative; } - .rail-new-chat::after { - content: 'New'; + /* Sleek hover labels for the mini sidebar. Use a real child span instead + of ::before/::after so status dots and minimized indicators keep working. */ + .rail-hover-label { position: absolute; left: 100%; top: 50%; @@ -1143,7 +1141,7 @@ body.bg-pattern-sparkles { z-index: 20; transition: opacity 0.25s ease, transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1); } - .rail-new-chat:hover::after { + .icon-rail-btn:hover .rail-hover-label { opacity: 1; transform: translateY(-50%) translateX(0); }