|
|
|
|
|
|
| import Storage from './storage.js';
|
|
|
| function clearFreshComposerRestore() {
|
| const msgInput = document.getElementById('message');
|
| if (!msgInput) return;
|
| const hasSessionTarget = !!(window.location.hash || Storage.get('lastSessionId'));
|
| if (hasSessionTarget) return;
|
| if (msgInput.value) {
|
| msgInput.value = '';
|
| msgInput.dispatchEvent(new Event('input', { bubbles: true }));
|
| }
|
| }
|
|
|
| clearFreshComposerRestore();
|
| window.addEventListener('pageshow', clearFreshComposerRestore);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| (async () => {
|
| try {
|
| const res = await fetch('/api/auth/status', { credentials: 'same-origin' });
|
| if (!res.ok) return;
|
| const data = await res.json().catch(() => ({}));
|
| const liveUser = (data && data.username) || '';
|
| if (!liveUser) return;
|
| const KEY = 'odysseus-auth-user';
|
| const cachedUser = localStorage.getItem(KEY);
|
| if (cachedUser && cachedUser !== liveUser) {
|
| const _keepKeys = new Set(['odysseus-last-user', KEY]);
|
| const toRemove = [];
|
| for (let i = 0; i < localStorage.length; i++) {
|
| const k = localStorage.key(i);
|
| if (k && !_keepKeys.has(k)) toRemove.push(k);
|
| }
|
| toRemove.forEach(k => localStorage.removeItem(k));
|
| sessionStorage.clear();
|
| clearFreshComposerRestore();
|
| }
|
| localStorage.setItem(KEY, liveUser);
|
|
|
|
|
|
|
|
|
| try {
|
| const privs = (data && data.privileges) || {};
|
| const hideOn = (selector, allowed) => {
|
| if (allowed === undefined || allowed === true) return;
|
| document.querySelectorAll(selector).forEach(el => {
|
| el.style.display = 'none';
|
| });
|
| };
|
|
|
| hideOn('#overflow-doc-btn, #tool-doc-btn', privs.can_use_documents);
|
|
|
| hideOn('#tool-research-btn, #research-toggle-btn', privs.can_use_research);
|
|
|
| hideOn('#tool-memory-btn', privs.can_manage_memory);
|
|
|
| if (privs.can_use_agent === false) {
|
| const _agent = document.getElementById('mode-agent-btn');
|
| const _chat = document.getElementById('mode-chat-btn');
|
| if (_agent) _agent.style.display = 'none';
|
| if (_chat) { _chat.classList.add('active'); _chat.click?.(); }
|
| }
|
| } catch (_) { }
|
| } catch (_) { }
|
| })();
|
|
|
| |
| |
| |
|
|
| {
|
| const KEY = Storage.KEYS.SIDEBAR_COLLAPSED;
|
| const saved = Storage.getJSON(KEY, {});
|
| const _defaultCollapsed = { 'sessions-section': true };
|
| document.querySelectorAll('.sidebar .section').forEach((section) => {
|
| const id = section.id;
|
| if (!id) return;
|
| const shouldCollapse = (id in saved) ? saved[id] : !!_defaultCollapsed[id];
|
| if (shouldCollapse) section.classList.add('collapsed');
|
| });
|
|
|
|
|
|
|
| const sessionsSection = document.getElementById('sessions-section');
|
| if (sessionsSection) {
|
| new MutationObserver(() => {
|
| if (!sessionsSection.classList.contains('collapsed')) {
|
| const dot = document.getElementById('chats-notif-dot');
|
| if (dot) dot.style.display = 'none';
|
| }
|
| }).observe(sessionsSection, { attributes: true, attributeFilter: ['class'] });
|
| }
|
| }
|
|
|
| |
| |
| |
| |
|
|
| {
|
| const rail = document.getElementById('icon-rail');
|
| const sidebar = document.getElementById('sidebar');
|
| const root = document.documentElement;
|
| const _measure = (el) => {
|
| if (!el) return null;
|
| const cs = window.getComputedStyle(el);
|
| const hidden = cs.display === 'none' || cs.visibility === 'hidden';
|
| if (hidden) return 0;
|
| return Math.round(el.getBoundingClientRect().width);
|
| };
|
| const _sync = () => {
|
|
|
| const rw = _measure(rail);
|
| if (rw === null) {
|
| root.style.removeProperty('--icon-rail-w');
|
| } else if (rw > 0) {
|
| root.style.setProperty('--icon-rail-w', rw + 'px');
|
| } else {
|
|
|
|
|
| const cs = rail && window.getComputedStyle(rail);
|
| const hidden = !cs || cs.display === 'none' || cs.visibility === 'hidden';
|
| if (hidden) {
|
| root.style.setProperty('--icon-rail-w', '0px');
|
| } else {
|
| root.style.removeProperty('--icon-rail-w');
|
| requestAnimationFrame(_sync);
|
| return;
|
| }
|
| }
|
|
|
|
|
| const sw = _measure(sidebar);
|
| if (sw === null) {
|
| root.style.removeProperty('--sidebar-w');
|
| } else {
|
| root.style.setProperty('--sidebar-w', sw + 'px');
|
| }
|
| };
|
| _sync();
|
| if (typeof ResizeObserver !== 'undefined') {
|
| const ro = new ResizeObserver(_sync);
|
| if (rail) ro.observe(rail);
|
| if (sidebar) ro.observe(sidebar);
|
| }
|
|
|
|
|
|
|
| if (sidebar && typeof MutationObserver !== 'undefined') {
|
| new MutationObserver(_sync).observe(sidebar, { attributes: true, attributeFilter: ['class', 'style'] });
|
| }
|
| if (rail && typeof MutationObserver !== 'undefined') {
|
| new MutationObserver(_sync).observe(rail, { attributes: true, attributeFilter: ['class', 'style'] });
|
| }
|
| window.addEventListener('resize', _sync);
|
| }
|
|
|
| |
|
|
| {
|
| const root = document.documentElement;
|
| const chatBar = document.querySelector('.chat-input-bar');
|
| const attachStrip = document.getElementById('attach-strip');
|
| const chatContainer = document.getElementById('chat-container');
|
| const _syncComposerClearance = () => {
|
| let top = window.innerHeight;
|
| for (const el of [attachStrip, chatBar]) {
|
| if (!el) continue;
|
| const rect = el.getBoundingClientRect();
|
| if (rect.height > 0) top = Math.min(top, rect.top);
|
| }
|
| const clearance = Math.max(12, Math.ceil(window.innerHeight - top + 8));
|
| root.style.setProperty('--composer-clearance', clearance + 'px');
|
| };
|
| requestAnimationFrame(_syncComposerClearance);
|
| if (typeof ResizeObserver !== 'undefined') {
|
| const ro = new ResizeObserver(_syncComposerClearance);
|
| if (chatBar) ro.observe(chatBar);
|
| if (attachStrip) ro.observe(attachStrip);
|
| }
|
| if (chatContainer && typeof MutationObserver !== 'undefined') {
|
| new MutationObserver(_syncComposerClearance).observe(chatContainer, {
|
| attributes: true,
|
| attributeFilter: ['class'],
|
| });
|
| }
|
| if (chatBar) chatBar.addEventListener('transitionend', _syncComposerClearance);
|
| window.addEventListener('resize', _syncComposerClearance);
|
| }
|
|
|
|
|
| {
|
| const sidebar = document.getElementById('sidebar');
|
| const handle = document.getElementById('sidebar-resize-handle');
|
| const railHandle = document.getElementById('rail-resize-handle');
|
| const iconRail = document.getElementById('icon-rail');
|
| if (sidebar && handle) {
|
|
|
| const STORAGE_KEY = Storage.KEYS.SIDEBAR_WIDTH;
|
| const MIN_WIDTH = 200;
|
| const MAX_WIDTH = 700;
|
| const COLLAPSE_THRESHOLD = 150;
|
|
|
| function getSavedWidth() {
|
| const w = parseInt(Storage.get(STORAGE_KEY, '340'), 10);
|
| return (w >= MIN_WIDTH && w <= MAX_WIDTH) ? w : 340;
|
| }
|
|
|
|
|
| const savedWidth = Storage.get(STORAGE_KEY);
|
| if (savedWidth) {
|
| const w = parseInt(savedWidth, 10);
|
| if (w >= MIN_WIDTH && w <= MAX_WIDTH) sidebar.style.width = w + 'px';
|
| }
|
|
|
| let startX, startWidth, isRight, collapsed, expanding;
|
|
|
|
|
| handle.addEventListener('mousedown', (e) => {
|
| e.preventDefault();
|
| expanding = false;
|
| isRight = sidebar.classList.contains('right-side');
|
| startX = e.clientX;
|
| startWidth = sidebar.getBoundingClientRect().width;
|
| collapsed = false;
|
| sidebar.classList.add('resizing');
|
| handle.classList.add('dragging');
|
| document.addEventListener('mousemove', onDrag);
|
| document.addEventListener('mouseup', stopDrag);
|
| });
|
|
|
|
|
| if (railHandle) {
|
| railHandle.addEventListener('mousedown', (e) => {
|
| e.preventDefault();
|
| expanding = true;
|
| isRight = sidebar.classList.contains('right-side') ||
|
| iconRail.classList.contains('right-side');
|
| startX = e.clientX;
|
| collapsed = false;
|
|
|
|
|
| sidebar.classList.remove('hidden');
|
| sidebar.classList.add('resizing');
|
| sidebar.style.width = '0px';
|
| sidebar.style.opacity = '0.3';
|
| railHandle.classList.add('dragging');
|
|
|
| document.addEventListener('mousemove', onExpandDrag);
|
| document.addEventListener('mouseup', stopExpandDrag);
|
| });
|
| }
|
|
|
| function onDrag(e) {
|
| const delta = isRight ? (startX - e.clientX) : (e.clientX - startX);
|
| const rawWidth = startWidth + delta;
|
|
|
| if (rawWidth < COLLAPSE_THRESHOLD) {
|
| sidebar.style.width = Math.max(0, rawWidth) + 'px';
|
| sidebar.style.opacity = Math.max(0.2, rawWidth / COLLAPSE_THRESHOLD);
|
| collapsed = true;
|
| } else {
|
| const newWidth = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, rawWidth));
|
| sidebar.style.width = newWidth + 'px';
|
| sidebar.style.opacity = '';
|
| collapsed = false;
|
| }
|
| }
|
|
|
| function stopDrag() {
|
| sidebar.classList.remove('resizing');
|
| handle.classList.remove('dragging');
|
| sidebar.style.opacity = '';
|
| document.removeEventListener('mousemove', onDrag);
|
| document.removeEventListener('mouseup', stopDrag);
|
|
|
| if (collapsed) {
|
| sidebar.style.width = '';
|
| sidebar.classList.add('hidden');
|
| if (typeof syncRailSide === 'function') syncRailSide();
|
| } else {
|
| const finalWidth = parseInt(sidebar.style.width, 10);
|
| if (finalWidth >= MIN_WIDTH) {
|
| Storage.set(STORAGE_KEY, String(finalWidth));
|
| }
|
| }
|
| }
|
|
|
| function onExpandDrag(e) {
|
| const delta = isRight ? (startX - e.clientX) : (e.clientX - startX);
|
| const rawWidth = Math.max(0, delta);
|
|
|
| if (rawWidth < COLLAPSE_THRESHOLD) {
|
| sidebar.style.width = rawWidth + 'px';
|
| sidebar.style.opacity = Math.max(0.3, rawWidth / COLLAPSE_THRESHOLD);
|
| collapsed = true;
|
| } else {
|
| const newWidth = Math.min(MAX_WIDTH, Math.max(MIN_WIDTH, rawWidth));
|
| sidebar.style.width = newWidth + 'px';
|
| sidebar.style.opacity = '';
|
| collapsed = false;
|
| }
|
| }
|
|
|
| function stopExpandDrag() {
|
| sidebar.classList.remove('resizing');
|
| sidebar.style.opacity = '';
|
| if (railHandle) railHandle.classList.remove('dragging');
|
| document.removeEventListener('mousemove', onExpandDrag);
|
| document.removeEventListener('mouseup', stopExpandDrag);
|
|
|
| if (collapsed) {
|
|
|
| sidebar.style.width = '';
|
| sidebar.classList.add('hidden');
|
| if (typeof syncRailSide === 'function') syncRailSide();
|
| } else {
|
|
|
| const finalWidth = parseInt(sidebar.style.width, 10);
|
| if (finalWidth >= MIN_WIDTH) {
|
| Storage.set(STORAGE_KEY, String(finalWidth));
|
| }
|
| if (typeof syncRailSide === 'function') syncRailSide();
|
| }
|
| }
|
|
|
| }
|
| }
|
|
|
|
|
| {
|
| if (window.visualViewport) {
|
| let _lastVVHeight = window.visualViewport.height;
|
| window.visualViewport.addEventListener('resize', function() {
|
| const vv = window.visualViewport;
|
| const keyboardOpened = vv.height < _lastVVHeight - 50;
|
| _lastVVHeight = vv.height;
|
| if (keyboardOpened) {
|
| var chatHistory = document.getElementById('chat-history');
|
| if (chatHistory) {
|
| requestAnimationFrame(function() {
|
| chatHistory.scrollTop = chatHistory.scrollHeight;
|
| });
|
| }
|
| }
|
| });
|
| }
|
|
|
|
|
| if ('ontouchstart' in window) {
|
| document.addEventListener('DOMContentLoaded', function() {
|
| var _msgInput = document.getElementById('message');
|
| if (!_msgInput) return;
|
| _msgInput.addEventListener('focus', function() {
|
| var welcome = document.getElementById('welcome-screen');
|
| if (welcome && !welcome.classList.contains('hidden')) {
|
| welcome.style.transition = 'opacity 0.2s ease, transform 0.2s ease';
|
| welcome.style.opacity = '0';
|
| welcome.style.transform = 'translate(-50%, -50%) scale(0.92)';
|
| }
|
| });
|
| _msgInput.addEventListener('blur', function() {
|
| var welcome = document.getElementById('welcome-screen');
|
| if (welcome && !welcome.classList.contains('hidden')) {
|
| welcome.style.transition = 'opacity 0.3s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)';
|
| welcome.style.opacity = '';
|
| welcome.style.transform = '';
|
| }
|
| });
|
| });
|
| }
|
| }
|
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| (function () {
|
| let fired = false;
|
| function release() {
|
| if (fired) return;
|
| fired = true;
|
| requestAnimationFrame(() =>
|
| requestAnimationFrame(() => document.body.classList.add('welcome-ready'))
|
| );
|
| }
|
| try { if (document.fonts && document.fonts.ready) document.fonts.ready.then(release); } catch (_) {}
|
| if (document.readyState === 'complete') release();
|
| else window.addEventListener('load', release);
|
| setTimeout(release, 1200);
|
| })();
|
|
|