mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-10 17:02:20 +02:00
Squash Odysseus development history
This commit is contained in:
+90
-18
@@ -34,6 +34,46 @@ export function initSidebarLayout(Storage, opts) {
|
||||
// ── Icon rail + sidebar toggle ──
|
||||
const iconRail = document.getElementById('icon-rail');
|
||||
const hamburgerBtn = document.getElementById('hamburger-btn');
|
||||
const SIDEBAR_MODE_KEY = 'odysseus-sidebar-mode';
|
||||
|
||||
function _setSidebarModeClasses(mode) {
|
||||
document.documentElement.classList.remove('ody-mobile-startup-sidebar-hidden');
|
||||
document.documentElement.classList.toggle('ody-sidebar-mini', mode === 'mini');
|
||||
document.documentElement.classList.toggle('ody-sidebar-off', mode === 'off');
|
||||
}
|
||||
|
||||
function _saveSidebarMode(mode) {
|
||||
try { localStorage.setItem(SIDEBAR_MODE_KEY, mode); } catch (_) {}
|
||||
_setSidebarModeClasses(mode);
|
||||
}
|
||||
|
||||
function _applyStoredSidebarMode() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
if (!sidebar) return;
|
||||
if (window.innerWidth < 768 && document.getElementById('app-loader')) {
|
||||
sidebar.classList.add('hidden');
|
||||
if (iconRail) {
|
||||
iconRail.classList.add('rail-hidden');
|
||||
iconRail.classList.remove('mobile-mini');
|
||||
iconRail.style.cssText = '';
|
||||
}
|
||||
_setSidebarModeClasses('off');
|
||||
return;
|
||||
}
|
||||
let mode = 'full';
|
||||
try { mode = localStorage.getItem(SIDEBAR_MODE_KEY) || 'full'; } catch (_) {}
|
||||
if (mode === 'mini') {
|
||||
sidebar.classList.add('hidden');
|
||||
if (iconRail) iconRail.classList.remove('rail-hidden');
|
||||
} else if (mode === 'off') {
|
||||
sidebar.classList.add('hidden');
|
||||
if (iconRail) iconRail.classList.add('rail-hidden');
|
||||
} else {
|
||||
sidebar.classList.remove('hidden');
|
||||
if (iconRail) iconRail.classList.remove('rail-hidden');
|
||||
}
|
||||
_setSidebarModeClasses(mode);
|
||||
}
|
||||
|
||||
function _syncRailSideCore() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
@@ -62,6 +102,7 @@ export function initSidebarLayout(Storage, opts) {
|
||||
document.body.classList.toggle('hamburger-left', !isRight);
|
||||
document.body.classList.toggle('hamburger-only', sidebarHidden && railHidden);
|
||||
document.body.classList.toggle('sidebar-collapsed', sidebarHidden);
|
||||
_setSidebarModeClasses(!sidebarHidden ? 'full' : (railHidden ? 'off' : 'mini'));
|
||||
}
|
||||
// Keep incognito button clear of hamburger
|
||||
const incogBtn = document.getElementById('incognito-btn');
|
||||
@@ -82,6 +123,7 @@ export function initSidebarLayout(Storage, opts) {
|
||||
if (Storage.get(Storage.KEYS.SIDEBAR_SIDE) === 'right') {
|
||||
document.getElementById('sidebar').classList.add('right-side');
|
||||
}
|
||||
_applyStoredSidebarMode();
|
||||
syncRailSide();
|
||||
|
||||
// In-sidebar toggle button — same behavior as hamburger
|
||||
@@ -92,10 +134,11 @@ export function initSidebarLayout(Storage, opts) {
|
||||
});
|
||||
}
|
||||
|
||||
// New chat buttons — same as clicking brand
|
||||
// Header-only new-chat aliases. #sidebar-new-chat-btn is wired in app.js
|
||||
// because it needs the full default-model/pending-chat flow; wiring it here
|
||||
// as well caused duplicate click handling and occasional no-op/race behavior.
|
||||
const chatNewBtn = document.getElementById('chat-new-btn');
|
||||
const sidebarNewChat = document.getElementById('sidebar-new-chat-btn');
|
||||
[chatNewBtn, sidebarNewChat].forEach(btn => {
|
||||
[chatNewBtn].forEach(btn => {
|
||||
if (btn) btn.addEventListener('click', () => {
|
||||
const brandBtn = document.getElementById('sidebar-brand-btn');
|
||||
if (brandBtn) brandBtn.click();
|
||||
@@ -103,9 +146,25 @@ export function initSidebarLayout(Storage, opts) {
|
||||
});
|
||||
|
||||
// Hamburger cycles: full sidebar → mini → off → full
|
||||
// Shift-click swaps sidebar side
|
||||
let _userToggledSidebar = false;
|
||||
let _wasAutoCollapsed = false;
|
||||
let _wasMobileViewport = window.innerWidth < 768;
|
||||
|
||||
function _restoreDesktopSidebarSide() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
if (!sidebar) return;
|
||||
const wantRight = Storage.get(Storage.KEYS.SIDEBAR_SIDE) === 'right';
|
||||
const changed = sidebar.classList.contains('right-side') !== wantRight;
|
||||
sidebar.classList.toggle('right-side', wantRight);
|
||||
if (iconRail) {
|
||||
iconRail.classList.remove('mobile-mini');
|
||||
iconRail.style.cssText = '';
|
||||
}
|
||||
if (changed && documentModule && documentModule.swapSide) {
|
||||
try { documentModule.swapSide(); } catch (_) {}
|
||||
}
|
||||
syncRailSide();
|
||||
}
|
||||
|
||||
// Deliberate "open the sidebar" used by the mobile swipe gesture (wired at
|
||||
// module scope). It MUST set _userToggledSidebar so the auto-collapse
|
||||
@@ -122,13 +181,16 @@ export function initSidebarLayout(Storage, opts) {
|
||||
if (window.innerWidth < 768 && cc && cc.classList.contains('compare-active')) return;
|
||||
_userToggledSidebar = true;
|
||||
// Optionally place the sidebar on a specific edge (the swipe gesture passes
|
||||
// the direction). Persist it + re-anchor the doc panel, same as a
|
||||
// shift-click on the hamburger.
|
||||
// the direction). Persist it + re-anchor the doc panel.
|
||||
if (side === 'left' || side === 'right') {
|
||||
const wantRight = side === 'right';
|
||||
if (sidebar.classList.contains('right-side') !== wantRight) {
|
||||
sidebar.classList.toggle('right-side', wantRight);
|
||||
try { Storage.set(Storage.KEYS.SIDEBAR_SIDE, side); } catch (_) {}
|
||||
// Swipe-selected sides are temporary on compact/mobile layouts. Do
|
||||
// not overwrite the user's desktop sidebar preference.
|
||||
if (window.innerWidth >= 768) {
|
||||
try { Storage.set(Storage.KEYS.SIDEBAR_SIDE, side); } catch (_) {}
|
||||
}
|
||||
if (documentModule && documentModule.swapSide) { try { documentModule.swapSide(); } catch (_) {} }
|
||||
}
|
||||
}
|
||||
@@ -143,13 +205,6 @@ export function initSidebarLayout(Storage, opts) {
|
||||
hamburgerBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
if (e.shiftKey) {
|
||||
sidebar.classList.toggle('right-side');
|
||||
Storage.set(Storage.KEYS.SIDEBAR_SIDE, sidebar.classList.contains('right-side') ? 'right' : 'left');
|
||||
syncRailSide();
|
||||
if (documentModule && documentModule.swapSide) documentModule.swapSide();
|
||||
return;
|
||||
}
|
||||
|
||||
_userToggledSidebar = true;
|
||||
const isSidebarVisible = !sidebar.classList.contains('hidden');
|
||||
@@ -162,6 +217,7 @@ export function initSidebarLayout(Storage, opts) {
|
||||
if (isSidebarVisible) {
|
||||
// Closing sidebar
|
||||
sidebar.classList.add('hidden');
|
||||
_saveSidebarMode('off');
|
||||
if (backdrop) backdrop.classList.remove('visible');
|
||||
} else {
|
||||
// Mobile: the hamburger always opens the sidebar from the RIGHT.
|
||||
@@ -177,11 +233,13 @@ export function initSidebarLayout(Storage, opts) {
|
||||
// Wait for keyboard dismiss to settle, then open
|
||||
setTimeout(() => {
|
||||
sidebar.classList.remove('hidden');
|
||||
_saveSidebarMode('full');
|
||||
if (backdrop) backdrop.classList.add('visible');
|
||||
syncRailSide();
|
||||
}, 250);
|
||||
} else {
|
||||
sidebar.classList.remove('hidden');
|
||||
_saveSidebarMode('full');
|
||||
if (backdrop) backdrop.classList.add('visible');
|
||||
}
|
||||
}
|
||||
@@ -192,10 +250,13 @@ export function initSidebarLayout(Storage, opts) {
|
||||
// Desktop: full sidebar ↔ mini (icon rail) — simple toggle
|
||||
if (isSidebarVisible) {
|
||||
sidebar.classList.add('hidden');
|
||||
if (iconRail) iconRail.classList.remove('rail-hidden');
|
||||
_saveSidebarMode('mini');
|
||||
} else {
|
||||
_wasAutoCollapsed = false;
|
||||
iconRail.classList.remove('rail-hidden');
|
||||
sidebar.classList.remove('hidden');
|
||||
_saveSidebarMode('full');
|
||||
}
|
||||
syncRailSide();
|
||||
});
|
||||
@@ -256,12 +317,23 @@ export function initSidebarLayout(Storage, opts) {
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
const isMobileViewport = window.innerWidth < 768;
|
||||
if (_wasMobileViewport && !isMobileViewport) _restoreDesktopSidebarSide();
|
||||
_wasMobileViewport = isMobileViewport;
|
||||
_userToggledSidebar = false; // allow auto-collapse on actual resize
|
||||
requestAnimationFrame(checkSidebarAutoCollapse);
|
||||
});
|
||||
// Also re-check when doc panel toggles
|
||||
new MutationObserver(() => requestAnimationFrame(checkSidebarAutoCollapse))
|
||||
.observe(document.body, { attributes: true, attributeFilter: ['class'] });
|
||||
// Re-check when the document split is opened or closed. Do not react to
|
||||
// every body class mutation: floating tools (notably Research) add their
|
||||
// own view class, and re-evaluating a narrow document split there caused a
|
||||
// hide → restore → hide loop in the sidebar.
|
||||
let _lastDocView = document.body.classList.contains('doc-view');
|
||||
new MutationObserver(() => {
|
||||
const docView = document.body.classList.contains('doc-view');
|
||||
if (docView === _lastDocView) return;
|
||||
_lastDocView = docView;
|
||||
requestAnimationFrame(checkSidebarAutoCollapse);
|
||||
}).observe(document.body, { attributes: true, attributeFilter: ['class'] });
|
||||
|
||||
// Auto-collapse on initial load if window is small
|
||||
if (window.innerWidth < AUTO_COLLAPSE_WIDTH) {
|
||||
@@ -492,7 +564,7 @@ function _initChatSwipeToOpenSidebar() {
|
||||
|
||||
// Areas where a horizontal drag means something else (their own scroll/drag).
|
||||
const EXCLUDE = [
|
||||
'#sidebar', '#icon-rail', '.modal', '.input-bar', '#message',
|
||||
'#sidebar', '#icon-rail', '.modal', '.input-bar', '.chat-input-bar', '#message',
|
||||
'#minimized-dock', '.minimized-dock-chip', '#dock-trash-zone',
|
||||
'pre', 'table', '.agent-tool-output', '.agent-thread-cmd',
|
||||
'input', 'textarea', 'select',
|
||||
|
||||
Reference in New Issue
Block a user