botserver/web/desktop/js/layout.js

164 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const sections = {
drive: 'drive/drive.html',
tasks: 'tasks/tasks.html',
mail: 'mail/mail.html',
chat: 'chat/chat.html',
};
const sectionCache = {};
function getBasePath() {
// All static assets (HTML, CSS, JS) are served from the site root.
// Returning a leading slash ensures URLs like "/drive/drive.html" resolve correctly
return '/';
}
// Preload chat CSS to avoid flash on first load
function preloadChatCSS() {
const chatCssPath = getBasePath() + 'chat/chat.css';
const existing = document.querySelector(`link[href="${chatCssPath}"]`);
if (!existing) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = chatCssPath;
document.head.appendChild(link);
}
}
async function loadSectionHTML(path) {
const fullPath = getBasePath() + path;
const response = await fetch(fullPath);
if (!response.ok) throw new Error('Failed to load section: ' + fullPath);
return await response.text();
}
async function switchSection(section) {
const mainContent = document.getElementById('main-content');
try {
const htmlPath = sections[section];
console.log('Loading section:', section, 'from', htmlPath);
// Resolve CSS path relative to the base directory.
const cssPath = getBasePath() + htmlPath.replace('.html', '.css');
// Preload chat CSS if the target is chat
if (section === 'chat') {
preloadChatCSS();
}
// Remove any existing section CSS
document.querySelectorAll('link[data-section-css]').forEach(link => link.remove());
// Load CSS first (skip if already loaded)
let cssLink = document.querySelector(`link[href="${cssPath}"]`);
if (!cssLink) {
cssLink = document.createElement('link');
cssLink.rel = 'stylesheet';
cssLink.href = cssPath;
cssLink.setAttribute('data-section-css', 'true');
document.head.appendChild(cssLink);
}
// Hide previously loaded sections and show the requested one
// Ensure a container exists for sections
let container = document.getElementById('section-container');
if (!container) {
container = document.createElement('div');
container.id = 'section-container';
mainContent.appendChild(container);
}
const targetDiv = document.getElementById(`section-${section}`);
if (targetDiv) {
// Section already loaded: hide others, show this one
container.querySelectorAll('.section').forEach(div => {
div.style.display = 'none';
});
targetDiv.style.display = 'block';
} else {
// Show loading placeholder inside the container
const loadingDiv = document.createElement('div');
loadingDiv.className = 'loading';
loadingDiv.textContent = 'Loading…';
container.appendChild(loadingDiv);
// Load HTML
const html = await loadSectionHTML(htmlPath);
// Create wrapper for the new section
const wrapper = document.createElement('div');
wrapper.id = `section-${section}`;
wrapper.className = 'section';
wrapper.innerHTML = html;
// Hide any existing sections
container.querySelectorAll('.section').forEach(div => {
div.style.display = 'none';
});
// Remove loading placeholder
container.removeChild(loadingDiv);
// Add the new section to the container and cache it
container.appendChild(wrapper);
sectionCache[section] = wrapper;
// Ensure the new section is visible with a fast GSAP fade-in
gsap.fromTo(
wrapper,
{ opacity: 0 },
{ opacity: 1, duration: 0.15, ease: 'power2.out' }
);
}
// Then load JS after HTML is inserted (skip if already loaded)
// Resolve JS path relative to the base directory.
const jsPath = getBasePath() + htmlPath.replace('.html', '.js');
const existingScript = document.querySelector(`script[src="${jsPath}"]`);
if (!existingScript) {
const script = document.createElement('script');
script.src = jsPath;
script.defer = true;
document.body.appendChild(script);
}
window.history.pushState({}, '', `#${section}`);
Alpine.initTree(mainContent);
const inputEl = document.getElementById('messageInput');
if (inputEl) {
inputEl.focus();
}
} catch (err) {
console.error('Error loading section:', err);
mainContent.innerHTML = `<div class="error">Failed to load ${section} section</div>`;
}
}
// Handle initial load based on URL hash
function getInitialSection() {
// 1⃣ Prefer hash fragment (e.g., #chat)
let section = window.location.hash.substring(1);
// 2⃣ Fallback to pathname segments (e.g., /chat)
if (!section) {
const parts = window.location.pathname.split('/').filter(p => p);
const last = parts[parts.length - 1];
if (['drive', 'tasks', 'mail', 'chat'].includes(last)) {
section = last;
}
}
// 3⃣ As a last resort, inspect the full URL for known sections
if (!section) {
const match = window.location.href.match(/\/(drive|tasks|mail|chat)(?:\.html)?(?:[?#]|$)/i);
if (match) {
section = match[1].toLowerCase();
}
}
// Default to chat if nothing matches
return section || 'chat';
}
window.addEventListener('DOMContentLoaded', () => {
switchSection(getInitialSection());
});
// Handle browser back/forward navigation
window.addEventListener('popstate', () => {
switchSection(getInitialSection());
});