Squash Odysseus development history

This commit is contained in:
pewdiepie-archdaemon
2026-09-11 06:04:19 +00:00
parent e5c99a5eee
commit 6ee6502010
2050 changed files with 538359 additions and 57745 deletions
+90 -18
View File
@@ -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',