Refactor header branding to use an image logo; enhance internal link handling and clean up unused code

This commit is contained in:
2025-01-30 07:24:58 +11:00
parent d33e470d15
commit 5ca1c080d3
4 changed files with 9 additions and 61 deletions

BIN
img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -27,7 +27,7 @@
<i class="fas fa-bars"></i> <i class="fas fa-bars"></i>
</button> </button>
<div class="title-text"> <div class="title-text">
<span class="brand">Litruv</span> <img src="./img/logo.png" alt="Litruv" class="brand-logo">
<span class="divider">/</span> <span class="divider">/</span>
<span class="page-title">Documentation</span> <span class="page-title">Documentation</span>
</div> </div>

View File

@@ -9,13 +9,9 @@ let markedPromise = new Promise((resolve) => {
async function initializeMarked() { async function initializeMarked() {
const marked = await markedPromise; const marked = await markedPromise;
// Create a new renderer
const renderer = new marked.Renderer(); const renderer = new marked.Renderer();
// Store the original link renderer
const originalLink = renderer.link.bind(renderer); const originalLink = renderer.link.bind(renderer);
// Override the link renderer
renderer.link = (href, title, text) => { renderer.link = (href, title, text) => {
const isExternal = href.startsWith('http') || href.startsWith('https'); const isExternal = href.startsWith('http') || href.startsWith('https');
const attrs = isExternal ? ' target="_blank" rel="noopener noreferrer"' : ''; const attrs = isExternal ? ' target="_blank" rel="noopener noreferrer"' : '';
@@ -26,7 +22,6 @@ async function initializeMarked() {
return link.replace(/^<a /, `<a${attrs} `); return link.replace(/^<a /, `<a${attrs} `);
}; };
// Set options with the custom renderer
marked.setOptions({ marked.setOptions({
highlight: function(code, lang) { highlight: function(code, lang) {
if (Prism.languages[lang]) { if (Prism.languages[lang]) {
@@ -72,7 +67,6 @@ function createFileIndexItem(doc, container, level = 0) {
} else { } else {
const link = document.createElement('a'); const link = document.createElement('a');
link.href = `?${doc.slug}`; link.href = `?${doc.slug}`;
// Use title from json or fallback to filename
const displayTitle = doc.title || doc.path.split('/').pop().replace('.md', ''); const displayTitle = doc.title || doc.path.split('/').pop().replace('.md', '');
link.textContent = displayTitle; link.textContent = displayTitle;
link.dataset.path = doc.path; link.dataset.path = doc.path;
@@ -82,7 +76,6 @@ function createFileIndexItem(doc, container, level = 0) {
e.preventDefault(); e.preventDefault();
loadDocument(doc.path); loadDocument(doc.path);
history.pushState(null, '', link.href); history.pushState(null, '', link.href);
// Hide sidebar on mobile after clicking a link
const leftSidebar = document.querySelector('.left-sidebar'); const leftSidebar = document.querySelector('.left-sidebar');
if (window.innerWidth <= 1000) { if (window.innerWidth <= 1000) {
leftSidebar.classList.remove('show'); leftSidebar.classList.remove('show');
@@ -141,15 +134,10 @@ async function loadIndex() {
const queryString = window.location.search; const queryString = window.location.search;
if (queryString) { if (queryString) {
const slug = queryString.substring(1); const slug = queryString.substring(1);
console.log('Loading document for slug:', slug);
const matchingDoc = findDocumentBySlug(data.documents, slug); const matchingDoc = findDocumentBySlug(data.documents, slug);
if (matchingDoc) { if (matchingDoc) {
console.log('Found matching document:', matchingDoc);
await loadDocument(matchingDoc.path); await loadDocument(matchingDoc.path);
} else {
console.warn('No matching document found for slug:', slug);
} }
} else { } else {
const defaultDoc = findDocumentBySlug(data.documents, 'welcome'); const defaultDoc = findDocumentBySlug(data.documents, 'welcome');
@@ -158,7 +146,6 @@ async function loadIndex() {
} }
} }
} catch (error) { } catch (error) {
console.error('Failed to load index:', error);
document.getElementById('document-content').innerHTML = document.getElementById('document-content').innerHTML =
'<div class="error">Failed to load documentation index.</div>'; '<div class="error">Failed to load documentation index.</div>';
} }
@@ -257,17 +244,13 @@ async function loadDocument(path) {
let rawContent = await response.text(); let rawContent = await response.text();
const { metadata, content } = extractMetadata(rawContent); const { metadata, content } = extractMetadata(rawContent);
// Get the title from index.json
const indexResponse = await fetch('index.json'); const indexResponse = await fetch('index.json');
const indexData = await indexResponse.json(); const indexData = await indexResponse.json();
const docEntry = findDocumentByPath(indexData.documents, path); const docEntry = findDocumentByPath(indexData.documents, path);
// Use title priority: frontmatter > json > filename
const titleContent = metadata.title || (docEntry && docEntry.title) || path.split('/').pop().replace('.md', ''); const titleContent = metadata.title || (docEntry && docEntry.title) || path.split('/').pop().replace('.md', '');
// Process Obsidian internal links before adding the title
let processedContent = content.replace(/\[\[(.*?)\]\]/g, (match, linkText) => { let processedContent = content.replace(/\[\[(.*?)\]\]/g, (match, linkText) => {
// Skip image/media processing
if (linkText.match(/\.(png|jpg|jpeg|gif|mp4|webm)$/i)) { if (linkText.match(/\.(png|jpg|jpeg|gif|mp4|webm)$/i)) {
return match; return match;
} }
@@ -281,7 +264,6 @@ async function loadDocument(path) {
processedContent = `# ${titleContent}\n\n${processedContent}`; processedContent = `# ${titleContent}\n\n${processedContent}`;
// Process images/media after internal links
const finalContent = processedContent.replace(/!\[\[(.*?)\]\]/g, (match, filename) => { const finalContent = processedContent.replace(/!\[\[(.*?)\]\]/g, (match, filename) => {
const mediaPath = `${basePath}/images/${filename}`; const mediaPath = `${basePath}/images/${filename}`;
@@ -295,33 +277,6 @@ async function loadDocument(path) {
return `\n![${filename}](${mediaPath})\n\n`; return `\n![${filename}](${mediaPath})\n\n`;
}); });
const description = metadata.description || `Documentation for ${titleContent}`;
const url = `${window.location.origin}${window.location.pathname}${window.location.search}`;
document.title = `Litruv / ${titleContent}`;
document.querySelector('meta[name="description"]').setAttribute('content', description);
document.querySelector('meta[property="og:title"]').setAttribute('content', titleContent);
document.querySelector('meta[property="og:description"]').setAttribute('content', description);
document.querySelector('meta[property="og:url"]').setAttribute('content', url);
document.querySelector('meta[name="twitter:title"]').setAttribute('content', titleContent);
document.querySelector('meta[name="twitter:description"]').setAttribute('content', description);
if (metadata.image) {
const imageUrl = `${window.location.origin}${window.location.pathname}${basePath}/images/${metadata.image}`;
document.querySelector('meta[property="og:image"]')?.remove();
document.querySelector('meta[name="twitter:image"]')?.remove();
const ogImage = document.createElement('meta');
ogImage.setAttribute('property', 'og:image');
ogImage.setAttribute('content', imageUrl);
document.head.appendChild(ogImage);
const twitterImage = document.createElement('meta');
twitterImage.setAttribute('name', 'twitter:image');
twitterImage.setAttribute('content', imageUrl);
document.head.appendChild(twitterImage);
}
document.title = `Litruv / ${titleContent}`; document.title = `Litruv / ${titleContent}`;
document.querySelector('.title-text .page-title').textContent = titleContent; document.querySelector('.title-text .page-title').textContent = titleContent;
@@ -342,11 +297,9 @@ async function loadDocument(path) {
const link = document.createElement('a'); const link = document.createElement('a');
link.href = `${window.location.pathname}${window.location.search}#${heading.id}`; link.href = `${window.location.pathname}${window.location.search}#${heading.id}`;
link.textContent = heading.textContent; link.textContent = heading.textContent;
// Add non-zero indentation
link.style.paddingLeft = (heading.tagName[1] * 15) + 'px'; link.style.paddingLeft = (heading.tagName[1] * 15) + 'px';
headingLinks.set(heading, link); headingLinks.set(heading, link);
// Add extra space to avoid scrolling beneath the header
heading.style.scrollMarginTop = 'var(--title-bar-height)'; heading.style.scrollMarginTop = 'var(--title-bar-height)';
link.onclick = (e) => { link.onclick = (e) => {
@@ -365,7 +318,6 @@ async function loadDocument(path) {
documentOutline.appendChild(link); documentOutline.appendChild(link);
}); });
// Replace the existing observer with this refined version
const observer = new IntersectionObserver((entries) => { const observer = new IntersectionObserver((entries) => {
const visibleHeadings = entries const visibleHeadings = entries
.filter(entry => entry.isIntersecting) .filter(entry => entry.isIntersecting)
@@ -383,11 +335,9 @@ async function loadDocument(path) {
headings.forEach(heading => observer.observe(heading)); headings.forEach(heading => observer.observe(heading));
// Clean up observer when loading new document
return () => observer.disconnect(); return () => observer.disconnect();
} catch (error) { } catch (error) {
console.error('Error loading document:', error);
document.getElementById('document-content').innerHTML = document.getElementById('document-content').innerHTML =
'<div class="error">Error loading document. Please try again.</div>'; '<div class="error">Error loading document. Please try again.</div>';
} }
@@ -429,16 +379,13 @@ function setupMobileMenu() {
} }
window.addEventListener('load', () => { window.addEventListener('load', () => {
console.log('Window loaded, initializing...');
setupMobileMenu(); setupMobileMenu();
loadIndex().catch(error => { loadIndex().catch(error => {
console.error('Failed to initialize:', error);
document.getElementById('document-content').innerHTML = document.getElementById('document-content').innerHTML =
'<div class="error">Failed to load documentation. Please try refreshing the page.</div>'; '<div class="error">Failed to load documentation. Please try refreshing the page.</div>';
}); });
}); });
// Globally intercept clicks on internal links
document.addEventListener('click', async (e) => { document.addEventListener('click', async (e) => {
const target = e.target.closest('a[data-internal="true"]'); const target = e.target.closest('a[data-internal="true"]');
if (target) { if (target) {
@@ -448,8 +395,6 @@ document.addEventListener('click', async (e) => {
const matchingDoc = findDocumentBySlug(indexData.documents, slug); const matchingDoc = findDocumentBySlug(indexData.documents, slug);
if (matchingDoc) { if (matchingDoc) {
await loadDocument(matchingDoc.path); await loadDocument(matchingDoc.path);
// Scroll to the top after loading
window.scrollTo({ top: 0, behavior: 'smooth' });
history.pushState(null, '', target.href); history.pushState(null, '', target.href);
} }
} }

View File

@@ -489,11 +489,14 @@ social-links a:hover {
gap: 0.5rem; gap: 0.5rem;
} }
.title-text .brand { .brand-logo {
color: var(--ifm-color-primary); height: 24px;
font-weight: 800; width: auto;
letter-spacing: -0.01em; display: block;
font-size: 1.2rem; }
.title-text .divider {
padding: 0 0 0 10px;
} }
.title-text .divider, .title-text .divider,