2
0
Files
logikonline 818c2db411
Some checks failed
Build and Release / Integration Tests (PostgreSQL) (push) Successful in 5m23s
Build and Release / Create Release (push) Successful in 0s
Build and Release / Lint (push) Successful in 6m8s
Build and Release / Unit Tests (push) Successful in 6m12s
Build and Release / Build Binaries (amd64, linux, linux-latest) (push) Successful in 3m37s
Build and Release / Build Binaries (amd64, windows, windows-latest) (push) Successful in 9h5m0s
Build and Release / Build Binaries (amd64, darwin, macos) (push) Failing after 1s
Build and Release / Build Binaries (arm64, darwin, macos) (push) Failing after 0s
Build and Release / Build Binary (linux/arm64) (push) Failing after 13m52s
feat(pages): add blog reading enhancements and gallery lightbox
Add reading progress bar, table of contents sidebar, and back-to-top button for blog post detail pages. TOC auto-generates from h1-h4 headings with smooth scroll navigation and active section highlighting. Sidebar expands on hover from left edge. Add lightbox for gallery images with keyboard navigation (arrows, escape) and captions. Back-to-top button appears after 400px scroll on all pages. All features use inline styles for portability and minimal CSS conflicts.
2026-03-16 21:43:46 -04:00

269 lines
8.5 KiB
Handlebars

<!DOCTYPE html>
<html lang="{{if .ActiveLang}}{{.ActiveLang}}{{else}}{{ctx.Locale.Lang}}{{end}}" data-theme="{{ctx.CurrentWebTheme.InternalName}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
{{if and .PageIsBlogDetail .BlogPost}}
<title>{{.BlogPost.Title}} - {{if .Config.Brand.Name}}{{.Config.Brand.Name}}{{else}}{{.Repository.Name}}{{end}}</title>
<meta name="description" content="{{if .BlogPost.Subtitle}}{{.BlogPost.Subtitle}}{{else}}{{.Repository.Description}}{{end}}">
{{else}}
<title>{{if .Config.Hero.Headline}}{{.Config.Hero.Headline}}{{else}}{{.Repository.Name}}{{end}} - {{.Repository.Owner.DisplayName}}</title>
<meta name="description" content="{{if .Config.Hero.Subheadline}}{{.Config.Hero.Subheadline}}{{else}}{{.Repository.Description}}{{end}}">
{{end}}
{{if and .PageIsBlogDetail .BlogPost}}
<meta property="og:title" content="{{.BlogPost.Title}}">
<meta property="og:description" content="{{if .BlogPost.Subtitle}}{{.BlogPost.Subtitle}}{{else if .Config.SEO.Description}}{{.Config.SEO.Description}}{{else}}{{.Repository.Description}}{{end}}">
<meta property="og:type" content="article">
<meta property="og:url" content="{{.PageURL}}">
<meta name="twitter:title" content="{{.BlogPost.Title}}">
<meta name="twitter:description" content="{{if .BlogPost.Subtitle}}{{.BlogPost.Subtitle}}{{else if .Config.SEO.Description}}{{.Config.SEO.Description}}{{else}}{{.Repository.Description}}{{end}}">
{{if .BlogPost.FeaturedImage}}
<meta property="og:image" content="{{.BlogPost.FeaturedImage.DownloadURL}}">
<meta name="twitter:image" content="{{.BlogPost.FeaturedImage.DownloadURL}}">
<meta name="twitter:card" content="summary_large_image">
{{else if .Config.SEO.UseMediaKitOG}}
<meta property="og:image" content="{{.SocialPreviewURL}}">
<meta name="twitter:image" content="{{.SocialPreviewURL}}">
<meta name="twitter:card" content="summary_large_image">
{{else if .Config.SEO.OGImage}}
<meta property="og:image" content="{{.Config.SEO.OGImage}}">
<meta name="twitter:image" content="{{.Config.SEO.OGImage}}">
<meta name="twitter:card" content="summary_large_image">
{{else}}
<meta name="twitter:card" content="summary">
{{end}}
{{else}}
<meta property="og:title" content="{{if .Config.SEO.Title}}{{.Config.SEO.Title}}{{else if .Config.Hero.Headline}}{{.Config.Hero.Headline}}{{else}}{{.Repository.Name}}{{end}}">
<meta property="og:description" content="{{if .Config.SEO.Description}}{{.Config.SEO.Description}}{{else if .Config.Hero.Subheadline}}{{.Config.Hero.Subheadline}}{{else}}{{.Repository.Description}}{{end}}">
<meta property="og:type" content="website">
<meta property="og:url" content="{{.PageURL}}">
<meta name="twitter:title" content="{{if .Config.SEO.Title}}{{.Config.SEO.Title}}{{else if .Config.Hero.Headline}}{{.Config.Hero.Headline}}{{else}}{{.Repository.Name}}{{end}}">
<meta name="twitter:description" content="{{if .Config.SEO.Description}}{{.Config.SEO.Description}}{{else if .Config.Hero.Subheadline}}{{.Config.Hero.Subheadline}}{{else}}{{.Repository.Description}}{{end}}">
{{if .Config.SEO.UseMediaKitOG}}
<meta property="og:image" content="{{.SocialPreviewURL}}">
<meta name="twitter:image" content="{{.SocialPreviewURL}}">
<meta name="twitter:card" content="summary_large_image">
{{else if .Config.SEO.OGImage}}
<meta property="og:image" content="{{.Config.SEO.OGImage}}">
<meta name="twitter:image" content="{{.Config.SEO.OGImage}}">
<meta name="twitter:card" content="summary_large_image">
{{else}}
<meta name="twitter:card" content="summary">
{{end}}
{{end}}
{{if .Config.SEO.TwitterSite}}<meta name="twitter:site" content="{{.Config.SEO.TwitterSite}}">{{end}}
{{if .Config.SEO.Keywords}}<meta name="keywords" content="{{StringUtils.Join .Config.SEO.Keywords ","}}">{{end}}
{{if .LangSwitcherEnabled}}{{range .AvailableLanguages}}
<link rel="alternate" hreflang="{{.}}" href="?lang={{.}}">{{end}}
{{end}}
{{if or .Config.Brand.UploadedFavicon .Config.Brand.FaviconURL}}
<link rel="icon" href="{{.Config.Brand.ResolvedFaviconURL}}">
{{else}}
<link rel="icon" href="{{AssetUrlPrefix}}/img/favicon.svg" type="image/svg+xml">
{{end}}
{{template "base/head_style" .}}
<style>
/* Pages standalone styles - no GitCaddy navbar */
:root {
--pages-primary: {{if .Config.Theme.PrimaryColor}}{{.Config.Theme.PrimaryColor}}{{else}}#4183c4{{end}};
--pages-secondary: {{if .Config.Theme.AccentColor}}{{.Config.Theme.AccentColor}}{{else}}#6c757d{{end}};
}
* { box-sizing: border-box; }
body.pages-body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
background: #fff;
}
.pages-landing {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.pages-main { flex: 1; }
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 24px;
}
/* Header styles */
.pages-header {
background: #fff;
border-bottom: 1px solid #e1e4e8;
padding: 16px 0;
position: sticky;
top: 0;
z-index: 100;
}
.pages-nav {
display: flex;
align-items: center;
justify-content: space-between;
}
.pages-nav-brand {
text-decoration: none;
font-weight: 600;
font-size: 1.25rem;
color: #24292e;
}
.pages-nav-logo { height: 32px; }
.pages-nav-links {
display: flex;
align-items: center;
gap: 24px;
}
.pages-nav-link {
color: #586069;
text-decoration: none;
font-size: 0.875rem;
}
.pages-nav-link:hover { color: var(--pages-primary); }
/* Hero styles */
.pages-hero {
padding: 80px 0;
text-align: center;
background: linear-gradient(135deg, #f6f8fa 0%, #fff 100%);
}
.pages-logo {
max-height: 80px;
margin-bottom: 24px;
}
.pages-title {
font-size: 3rem;
font-weight: 700;
margin: 0 0 16px;
color: #24292e;
}
.pages-tagline {
font-size: 1.25rem;
color: #586069;
margin: 0 0 32px;
max-width: 600px;
margin-left: auto;
margin-right: auto;
}
.pages-cta { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; }
/* Stats */
.pages-stats {
padding: 48px 0;
background: #f6f8fa;
}
.pages-stats-grid {
display: flex;
justify-content: center;
gap: 48px;
flex-wrap: wrap;
}
.pages-stat {
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.pages-stat-value {
font-size: 2rem;
font-weight: 700;
color: #24292e;
}
.pages-stat-label {
color: #586069;
font-size: 0.875rem;
}
/* README */
.pages-readme {
padding: 64px 0;
}
.pages-readme .markup {
max-width: 800px;
margin: 0 auto;
}
/* Footer */
.pages-footer {
background: #24292e;
color: #fff;
padding: 48px 0 24px;
margin-top: auto;
}
.pages-footer-links {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 32px;
margin-bottom: 32px;
}
.pages-footer-title {
font-size: 0.875rem;
font-weight: 600;
margin: 0 0 16px;
color: #fff;
}
.pages-footer-list {
list-style: none;
padding: 0;
margin: 0;
}
.pages-footer-list li { margin-bottom: 8px; }
.pages-footer-list a {
color: #959da5;
text-decoration: none;
font-size: 0.875rem;
}
.pages-footer-list a:hover { color: #fff; }
.pages-footer-bottom {
text-align: center;
padding-top: 24px;
border-top: 1px solid #444d56;
}
.pages-footer-copyright, .pages-footer-powered {
color: #959da5;
font-size: 0.75rem;
margin: 4px 0;
}
.pages-footer-powered a { color: #79b8ff; }
/* Language switcher */
.pages-lang-switcher {
position: relative;
display: inline-block;
}
.pages-lang-btn {
background: none;
border: 1px solid currentColor;
border-radius: 6px;
padding: 4px 10px;
font-size: 0.8rem;
cursor: pointer;
color: inherit;
display: flex;
align-items: center;
gap: 6px;
opacity: 0.8;
}
.pages-lang-btn:hover { opacity: 1; }
.pages-lang-dropdown {
display: none;
position: absolute;
top: 100%;
right: 0;
background: #fff;
border: 1px solid #e1e4e8;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
min-width: 140px;
z-index: 200;
margin-top: 4px;
overflow: hidden;
}
.pages-lang-dropdown.open { display: block; }
.pages-lang-option {
display: block;
padding: 8px 14px;
color: #24292e;
text-decoration: none;
font-size: 0.85rem;
}
.pages-lang-option:hover { background: #f6f8fa; }
.pages-lang-option.active { font-weight: 600; color: var(--pages-primary); }
</style>
</head>
<body class="pages-body">