You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
8.1 KiB
182 lines
8.1 KiB
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>{{.Title}} - {{.SiteName}}</title> |
|
{{if .Description}}<meta name="description" content="{{.Description}}">{{end}} |
|
{{if .Keywords}}<meta name="keywords" content="{{.Keywords}}">{{end}} |
|
|
|
<!-- OpenGraph / Facebook --> |
|
<meta property="og:type" content="{{if .OGType}}{{.OGType}}{{else}}website{{end}}"> |
|
<meta property="og:url" content="{{.CanonicalURL}}"> |
|
<meta property="og:title" content="{{.Title}} - {{.SiteName}}"> |
|
{{if .Description}}<meta property="og:description" content="{{.Description}}">{{end}} |
|
<meta property="og:image" content="{{.OGImage}}"> |
|
|
|
<!-- Twitter --> |
|
<meta name="twitter:card" content="summary_large_image"> |
|
<meta name="twitter:url" content="{{.CanonicalURL}}"> |
|
<meta name="twitter:title" content="{{.Title}} - {{.SiteName}}"> |
|
{{if .Description}}<meta name="twitter:description" content="{{.Description}}">{{end}} |
|
<meta name="twitter:image" content="{{.OGImage}}"> |
|
|
|
<link rel="canonical" href="{{.CanonicalURL}}"> |
|
<link rel="icon" type="image/svg+xml" href="/static/GitCitadel_Icon_Black.svg"> |
|
<link rel="stylesheet" href="/static/css/main.css"> |
|
<link rel="stylesheet" href="/static/css/responsive.css"> |
|
<link rel="stylesheet" href="/static/css/print.css" media="print"> |
|
<script src="https://unpkg.com/lucide@latest"></script> |
|
|
|
{{if .StructuredData}}<script type="application/ld+json">{{.StructuredData}}</script>{{end}} |
|
</head> |
|
<body> |
|
<a href="#main-content" class="skip-link">Skip to main content</a> |
|
|
|
<header> |
|
<nav class="navbar" role="navigation" aria-label="Main navigation"> |
|
<div class="nav-container"> |
|
<a href="/" class="logo"> |
|
<img src="/static/GitCitadel_Icon_Gradient.svg" alt="GitCitadel Logo" class="logo-icon"> |
|
<span class="logo-text">{{.SiteName}}</span> |
|
</a> |
|
|
|
<button class="mobile-menu-toggle" aria-label="Toggle menu" aria-expanded="false"> |
|
<span></span> |
|
<span></span> |
|
<span></span> |
|
</button> |
|
|
|
<ul class="nav-menu"> |
|
<li><a href="/"><i data-lucide="home" class="icon-inline"></i> Home</a></li> |
|
<li><a href="/wiki"><i data-lucide="book-open" class="icon-inline"></i> About The Project</a></li> |
|
<li class="dropdown"> |
|
<a href="/blog" class="dropdown-toggle"><i data-lucide="file-text" class="icon-inline"></i> Blog</a> |
|
<ul class="dropdown-menu"> |
|
{{range .BlogItems}} |
|
<li><a href="/blog#{{.DTag}}"><i data-lucide="file-text" class="icon-inline"></i> {{.Title}}</a></li> |
|
{{end}} |
|
</ul> |
|
</li> |
|
<li><a href="/ebooks"><i data-lucide="book" class="icon-inline"></i> E-Books</a></li> |
|
<li><a href="/contact"><i data-lucide="mail" class="icon-inline"></i> Contact</a></li> |
|
</ul> |
|
</div> |
|
</nav> |
|
</header> |
|
|
|
<div class="layout-container{{if and .WikiPages (or (eq .CanonicalURL (printf "%s/wiki" .SiteURL)) (hasPrefix .CanonicalURL (printf "%s/wiki/" .SiteURL)))}} wiki-layout{{end}}"> |
|
{{if and .WikiPages (or (eq .CanonicalURL (printf "%s/wiki" .SiteURL)) (hasPrefix .CanonicalURL (printf "%s/wiki/" .SiteURL)))}} |
|
{{template "wiki-sidebar" .}} |
|
{{end}} |
|
|
|
<main id="main-content" class="main-content"> |
|
{{block "content" .}}{{end}} |
|
</main> |
|
|
|
{{if and (or (eq .CanonicalURL .SiteURL) (eq .CanonicalURL (printf "%s/" .SiteURL))) (gt (len .FeedItems) 0)}} |
|
<aside class="feed-sidebar" aria-label="Recent notes"> |
|
{{template "feed" .}} |
|
</aside> |
|
{{end}} |
|
</div> |
|
|
|
<footer> |
|
<p><i data-lucide="copyright" class="icon-inline"></i> 2026 GitCitadel LLC. All Rights Reserved | <a href="/contact"><i data-lucide="mail" class="icon-inline"></i> Contact</a></p> |
|
</footer> |
|
|
|
<script> |
|
// Initialize Lucide icons |
|
let iconInitInProgress = false; |
|
let iconInitTimeout = null; |
|
|
|
function initIcons() { |
|
if (iconInitInProgress || typeof lucide === 'undefined') { |
|
return; |
|
} |
|
|
|
iconInitInProgress = true; |
|
try { |
|
// Only initialize icons on elements that haven't been processed yet |
|
// Lucide marks processed elements, so we can target unprocessed ones |
|
const unprocessedIcons = document.querySelectorAll('[data-lucide]:not([data-lucide-processed])'); |
|
if (unprocessedIcons.length > 0) { |
|
lucide.createIcons(); |
|
// Mark as processed to prevent re-processing |
|
unprocessedIcons.forEach(el => { |
|
el.setAttribute('data-lucide-processed', 'true'); |
|
}); |
|
} |
|
} catch (e) { |
|
console.error('Error initializing Lucide icons:', e); |
|
} finally { |
|
iconInitInProgress = false; |
|
} |
|
} |
|
|
|
// Debounced icon initialization to prevent excessive calls |
|
function debouncedInitIcons() { |
|
if (iconInitTimeout) { |
|
clearTimeout(iconInitTimeout); |
|
} |
|
iconInitTimeout = setTimeout(function() { |
|
if (!iconInitInProgress) { |
|
initIcons(); |
|
} |
|
}, 200); |
|
} |
|
|
|
// Initialize icons on page load |
|
if (document.readyState === 'loading') { |
|
document.addEventListener('DOMContentLoaded', function() { |
|
setTimeout(initIcons, 100); |
|
}); |
|
} else { |
|
setTimeout(initIcons, 100); |
|
} |
|
|
|
// Mobile menu toggle (optional - menu works without JS) |
|
if (document.querySelector('.mobile-menu-toggle')) { |
|
document.querySelector('.mobile-menu-toggle').addEventListener('click', function() { |
|
const menu = document.querySelector('.nav-menu'); |
|
const isExpanded = this.getAttribute('aria-expanded') === 'true'; |
|
this.setAttribute('aria-expanded', !isExpanded); |
|
menu.classList.toggle('active'); |
|
}); |
|
} |
|
|
|
// Reinitialize icons when DOM changes (for dynamically added content) |
|
// Only observe for new elements with data-lucide that haven't been processed |
|
const observer = new MutationObserver(function(mutations) { |
|
let hasNewIcons = false; |
|
for (const mutation of mutations) { |
|
if (mutation.type === 'childList') { |
|
for (const node of mutation.addedNodes) { |
|
if (node.nodeType === 1) { // Element node |
|
// Check if this node or its children have unprocessed icons |
|
if (node.hasAttribute && node.hasAttribute('data-lucide') && !node.hasAttribute('data-lucide-processed')) { |
|
hasNewIcons = true; |
|
break; |
|
} |
|
if (node.querySelector && node.querySelector('[data-lucide]:not([data-lucide-processed])')) { |
|
hasNewIcons = true; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
if (hasNewIcons) break; |
|
} |
|
if (hasNewIcons && !iconInitInProgress) { |
|
debouncedInitIcons(); |
|
} |
|
}); |
|
|
|
// Only observe for child additions, not attribute changes (to avoid Lucide's own DOM changes) |
|
observer.observe(document.body, { |
|
childList: true, |
|
subtree: true |
|
}); |
|
</script> |
|
</body> |
|
</html>
|
|
|