Load layout before Alpine and adjust chat CSS
This commit is contained in:
parent
9f1d74b101
commit
a823f319c3
3 changed files with 60 additions and 48 deletions
|
|
@ -618,7 +618,7 @@ footer {
|
||||||
}
|
}
|
||||||
/* Claude-style scrollbar - thin, subtle, always visible but more prominent on hover */
|
/* Claude-style scrollbar - thin, subtle, always visible but more prominent on hover */
|
||||||
#messages::-webkit-scrollbar {
|
#messages::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 12px;
|
||||||
}
|
}
|
||||||
#messages::-webkit-scrollbar-track {
|
#messages::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
@ -646,7 +646,7 @@ footer {
|
||||||
|
|
||||||
/* Fallback for other elements */
|
/* Fallback for other elements */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 10px;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/livekit-client/dist/livekit-client.umd.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/livekit-client/dist/livekit-client.umd.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
|
|
||||||
<script defer src="js/alpine.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
@ -46,7 +44,8 @@
|
||||||
<!-- Sections will be loaded dynamically -->
|
<!-- Sections will be loaded dynamically -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Load Layout Script - module scripts are loaded dynamically -->
|
<!-- Load Layout Script first, then Alpine - module scripts are loaded dynamically -->
|
||||||
<script src="js/layout.js"></script>
|
<script src="js/layout.js"></script>
|
||||||
|
<script src="js/alpine.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,24 @@
|
||||||
const sections = {
|
const sections = {
|
||||||
drive: 'drive/drive.html',
|
drive: "drive/drive.html",
|
||||||
tasks: 'tasks/tasks.html',
|
tasks: "tasks/tasks.html",
|
||||||
mail: 'mail/mail.html',
|
mail: "mail/mail.html",
|
||||||
chat: 'chat/chat.html',
|
chat: "chat/chat.html",
|
||||||
};
|
};
|
||||||
const sectionCache = {};
|
const sectionCache = {};
|
||||||
|
|
||||||
function getBasePath() {
|
function getBasePath() {
|
||||||
// All static assets (HTML, CSS, JS) are served from the site root.
|
// All static assets (HTML, CSS, JS) are served from the site root.
|
||||||
// Returning a leading slash ensures URLs like "/drive/drive.html" resolve correctly
|
// Returning a leading slash ensures URLs like "/drive/drive.html" resolve correctly
|
||||||
return '/';
|
return "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preload chat CSS to avoid flash on first load
|
// Preload chat CSS to avoid flash on first load
|
||||||
function preloadChatCSS() {
|
function preloadChatCSS() {
|
||||||
const chatCssPath = getBasePath() + 'chat/chat.css';
|
const chatCssPath = getBasePath() + "chat/chat.css";
|
||||||
const existing = document.querySelector(`link[href="${chatCssPath}"]`);
|
const existing = document.querySelector(`link[href="${chatCssPath}"]`);
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
const link = document.createElement('link');
|
const link = document.createElement("link");
|
||||||
link.rel = 'stylesheet';
|
link.rel = "stylesheet";
|
||||||
link.href = chatCssPath;
|
link.href = chatCssPath;
|
||||||
document.head.appendChild(link);
|
document.head.appendChild(link);
|
||||||
}
|
}
|
||||||
|
|
@ -27,43 +27,45 @@ function preloadChatCSS() {
|
||||||
async function loadSectionHTML(path) {
|
async function loadSectionHTML(path) {
|
||||||
const fullPath = getBasePath() + path;
|
const fullPath = getBasePath() + path;
|
||||||
const response = await fetch(fullPath);
|
const response = await fetch(fullPath);
|
||||||
if (!response.ok) throw new Error('Failed to load section: ' + fullPath);
|
if (!response.ok) throw new Error("Failed to load section: " + fullPath);
|
||||||
return await response.text();
|
return await response.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function switchSection(section) {
|
async function switchSection(section) {
|
||||||
const mainContent = document.getElementById('main-content');
|
const mainContent = document.getElementById("main-content");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const htmlPath = sections[section];
|
const htmlPath = sections[section];
|
||||||
console.log('Loading section:', section, 'from', htmlPath);
|
console.log("Loading section:", section, "from", htmlPath);
|
||||||
// Resolve CSS path relative to the base directory.
|
// Resolve CSS path relative to the base directory.
|
||||||
const cssPath = getBasePath() + htmlPath.replace('.html', '.css');
|
const cssPath = getBasePath() + htmlPath.replace(".html", ".css");
|
||||||
|
|
||||||
// Preload chat CSS if the target is chat
|
// Preload chat CSS if the target is chat
|
||||||
if (section === 'chat') {
|
if (section === "chat") {
|
||||||
preloadChatCSS();
|
preloadChatCSS();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any existing section CSS
|
// Remove any existing section CSS
|
||||||
document.querySelectorAll('link[data-section-css]').forEach(link => link.remove());
|
document
|
||||||
|
.querySelectorAll("link[data-section-css]")
|
||||||
|
.forEach((link) => link.remove());
|
||||||
|
|
||||||
// Load CSS first (skip if already loaded)
|
// Load CSS first (skip if already loaded)
|
||||||
let cssLink = document.querySelector(`link[href="${cssPath}"]`);
|
let cssLink = document.querySelector(`link[href="${cssPath}"]`);
|
||||||
if (!cssLink) {
|
if (!cssLink) {
|
||||||
cssLink = document.createElement('link');
|
cssLink = document.createElement("link");
|
||||||
cssLink.rel = 'stylesheet';
|
cssLink.rel = "stylesheet";
|
||||||
cssLink.href = cssPath;
|
cssLink.href = cssPath;
|
||||||
cssLink.setAttribute('data-section-css', 'true');
|
cssLink.setAttribute("data-section-css", "true");
|
||||||
document.head.appendChild(cssLink);
|
document.head.appendChild(cssLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide previously loaded sections and show the requested one
|
// Hide previously loaded sections and show the requested one
|
||||||
// Ensure a container exists for sections
|
// Ensure a container exists for sections
|
||||||
let container = document.getElementById('section-container');
|
let container = document.getElementById("section-container");
|
||||||
if (!container) {
|
if (!container) {
|
||||||
container = document.createElement('div');
|
container = document.createElement("div");
|
||||||
container.id = 'section-container';
|
container.id = "section-container";
|
||||||
mainContent.appendChild(container);
|
mainContent.appendChild(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,28 +73,28 @@ async function switchSection(section) {
|
||||||
|
|
||||||
if (targetDiv) {
|
if (targetDiv) {
|
||||||
// Section already loaded: hide others, show this one
|
// Section already loaded: hide others, show this one
|
||||||
container.querySelectorAll('.section').forEach(div => {
|
container.querySelectorAll(".section").forEach((div) => {
|
||||||
div.style.display = 'none';
|
div.style.display = "none";
|
||||||
});
|
});
|
||||||
targetDiv.style.display = 'block';
|
targetDiv.style.display = "block";
|
||||||
} else {
|
} else {
|
||||||
// Show loading placeholder inside the container
|
// Show loading placeholder inside the container
|
||||||
const loadingDiv = document.createElement('div');
|
const loadingDiv = document.createElement("div");
|
||||||
loadingDiv.className = 'loading';
|
loadingDiv.className = "loading";
|
||||||
loadingDiv.textContent = 'Loading…';
|
loadingDiv.textContent = "Loading…";
|
||||||
container.appendChild(loadingDiv);
|
container.appendChild(loadingDiv);
|
||||||
|
|
||||||
// Load HTML
|
// Load HTML
|
||||||
const html = await loadSectionHTML(htmlPath);
|
const html = await loadSectionHTML(htmlPath);
|
||||||
// Create wrapper for the new section
|
// Create wrapper for the new section
|
||||||
const wrapper = document.createElement('div');
|
const wrapper = document.createElement("div");
|
||||||
wrapper.id = `section-${section}`;
|
wrapper.id = `section-${section}`;
|
||||||
wrapper.className = 'section';
|
wrapper.className = "section";
|
||||||
wrapper.innerHTML = html;
|
wrapper.innerHTML = html;
|
||||||
|
|
||||||
// Hide any existing sections
|
// Hide any existing sections
|
||||||
container.querySelectorAll('.section').forEach(div => {
|
container.querySelectorAll(".section").forEach((div) => {
|
||||||
div.style.display = 'none';
|
div.style.display = "none";
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove loading placeholder
|
// Remove loading placeholder
|
||||||
|
|
@ -106,28 +108,37 @@ async function switchSection(section) {
|
||||||
gsap.fromTo(
|
gsap.fromTo(
|
||||||
wrapper,
|
wrapper,
|
||||||
{ opacity: 0 },
|
{ opacity: 0 },
|
||||||
{ opacity: 1, duration: 0.15, ease: 'power2.out' }
|
{ opacity: 1, duration: 0.15, ease: "power2.out" },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then load JS after HTML is inserted (skip if already loaded)
|
// Then load JS after HTML is inserted (skip if already loaded)
|
||||||
// Resolve JS path relative to the base directory.
|
// Resolve JS path relative to the base directory.
|
||||||
const jsPath = getBasePath() + htmlPath.replace('.html', '.js');
|
const jsPath = getBasePath() + htmlPath.replace(".html", ".js");
|
||||||
const existingScript = document.querySelector(`script[src="${jsPath}"]`);
|
const existingScript = document.querySelector(`script[src="${jsPath}"]`);
|
||||||
|
|
||||||
if (!existingScript) {
|
if (!existingScript) {
|
||||||
const script = document.createElement('script');
|
// Create script and wait for it to load before initializing Alpine
|
||||||
|
const script = document.createElement("script");
|
||||||
script.src = jsPath;
|
script.src = jsPath;
|
||||||
script.defer = true;
|
script.defer = true;
|
||||||
document.body.appendChild(script);
|
|
||||||
|
// Wait for script to load before initializing Alpine
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
script.onload = resolve;
|
||||||
|
script.onerror = reject;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
window.history.pushState({}, '', `#${section}`);
|
|
||||||
|
window.history.pushState({}, "", `#${section}`);
|
||||||
Alpine.initTree(mainContent);
|
Alpine.initTree(mainContent);
|
||||||
const inputEl = document.getElementById('messageInput');
|
const inputEl = document.getElementById("messageInput");
|
||||||
if (inputEl) {
|
if (inputEl) {
|
||||||
inputEl.focus();
|
inputEl.focus();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error loading section:', err);
|
console.error("Error loading section:", err);
|
||||||
mainContent.innerHTML = `<div class="error">Failed to load ${section} section</div>`;
|
mainContent.innerHTML = `<div class="error">Failed to load ${section} section</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -138,27 +149,29 @@ function getInitialSection() {
|
||||||
let section = window.location.hash.substring(1);
|
let section = window.location.hash.substring(1);
|
||||||
// 2️⃣ Fallback to pathname segments (e.g., /chat)
|
// 2️⃣ Fallback to pathname segments (e.g., /chat)
|
||||||
if (!section) {
|
if (!section) {
|
||||||
const parts = window.location.pathname.split('/').filter(p => p);
|
const parts = window.location.pathname.split("/").filter((p) => p);
|
||||||
const last = parts[parts.length - 1];
|
const last = parts[parts.length - 1];
|
||||||
if (['drive', 'tasks', 'mail', 'chat'].includes(last)) {
|
if (["drive", "tasks", "mail", "chat"].includes(last)) {
|
||||||
section = last;
|
section = last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 3️⃣ As a last resort, inspect the full URL for known sections
|
// 3️⃣ As a last resort, inspect the full URL for known sections
|
||||||
if (!section) {
|
if (!section) {
|
||||||
const match = window.location.href.match(/\/(drive|tasks|mail|chat)(?:\.html)?(?:[?#]|$)/i);
|
const match = window.location.href.match(
|
||||||
|
/\/(drive|tasks|mail|chat)(?:\.html)?(?:[?#]|$)/i,
|
||||||
|
);
|
||||||
if (match) {
|
if (match) {
|
||||||
section = match[1].toLowerCase();
|
section = match[1].toLowerCase();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Default to chat if nothing matches
|
// Default to chat if nothing matches
|
||||||
return section || 'chat';
|
return section || "chat";
|
||||||
}
|
}
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
window.addEventListener("DOMContentLoaded", () => {
|
||||||
switchSection(getInitialSection());
|
switchSection(getInitialSection());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle browser back/forward navigation
|
// Handle browser back/forward navigation
|
||||||
window.addEventListener('popstate', () => {
|
window.addEventListener("popstate", () => {
|
||||||
switchSection(getInitialSection());
|
switchSection(getInitialSection());
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue