Add static blog generator and initial blog posts

- Implement StaticBlogGenerator class for generating static HTML blog pages from markdown data.
- Create initial blog index page (blog.html) with links to posts.
- Add individual blog post pages for "Markdown Formatting Test", "Building Interactive Node Graphs", "Welcome to My Blog", "test d d d d", and "testddd".
- Include markdown rendering capabilities for formatting content in blog posts.
This commit is contained in:
2026-05-10 19:46:31 +10:00
parent b8e357e76b
commit 35b049b833
11 changed files with 996 additions and 7 deletions

View File

@@ -586,6 +586,248 @@ svg.world-image {
color: rgba(205, 214, 244, 0.9);
}
/* ─── Static Blog Pages ─────────────────────────────────────────────────────── */
body.blog-page {
min-height: 100vh;
overflow-y: auto;
overflow-x: hidden;
display: block;
background-image:
linear-gradient(var(--grid-color-strong) 2px, transparent 2px),
linear-gradient(90deg, var(--grid-color-strong) 2px, transparent 2px),
linear-gradient(var(--grid-color) 1px, transparent 1px),
linear-gradient(90deg, var(--grid-color) 1px, transparent 1px);
background-size:
calc(var(--grid-size) * 5) calc(var(--grid-size) * 5),
calc(var(--grid-size) * 5) calc(var(--grid-size) * 5),
var(--grid-size) var(--grid-size),
var(--grid-size) var(--grid-size);
background-repeat: repeat;
background-position: 0 0, 0 0, 0 0, 0 0;
background-color: var(--bg);
}
.blog-page .quick-links {
position: fixed;
top: 20px;
left: 20px;
}
.blog-shell {
min-height: 100vh;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 84px 16px 36px;
}
.blog-logo-link {
margin-bottom: 16px;
opacity: 0.92;
transition: opacity 0.2s ease;
}
.blog-logo-link:hover {
opacity: 1;
}
.blog-logo {
width: min(520px, 76vw);
max-width: 100%;
height: auto;
display: block;
}
.blog-card {
width: min(900px, 100%);
background: rgba(30, 30, 46, 0.92);
border: 1px solid rgba(205, 214, 244, 0.14);
border-radius: 16px;
box-shadow: 0 20px 70px rgba(0, 0, 0, 0.35);
backdrop-filter: blur(8px);
padding: 22px 24px 28px;
}
.blog-post-header {
border-bottom: 1px solid rgba(205, 214, 244, 0.12);
padding-bottom: 14px;
margin-bottom: 16px;
}
.blog-post-title {
margin: 0;
color: var(--ctp-text);
font-size: clamp(1.35rem, 3vw, 2rem);
line-height: 1.2;
}
.blog-post-meta,
.blog-index-subtitle,
.blog-index-item-meta {
margin-top: 8px;
color: rgba(186, 194, 222, 0.82);
font-size: 0.9rem;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.blog-post-tags,
.blog-index-tags {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 10px;
}
.blog-tag {
display: inline-flex;
align-items: center;
border: 1px solid rgba(137, 180, 250, 0.35);
background: rgba(137, 180, 250, 0.12);
color: var(--ctp-sky);
padding: 3px 8px;
border-radius: 999px;
font-size: 0.78rem;
}
.blog-post-content {
color: var(--ctp-subtext1);
font-size: 1rem;
line-height: 1.75;
user-select: text;
}
.blog-post-content .md-h1,
.blog-post-content .md-h2,
.blog-post-content .md-h3,
.blog-post-content .md-h4,
.blog-post-content .md-h5,
.blog-post-content .md-h6 {
color: var(--ctp-text);
margin-top: 1.2em;
margin-bottom: 0.4em;
line-height: 1.3;
}
.blog-post-content .md-h1 { font-size: 1.9rem; }
.blog-post-content .md-h2 { font-size: 1.55rem; }
.blog-post-content .md-h3 { font-size: 1.3rem; }
.blog-post-content .md-h4 { font-size: 1.1rem; }
.blog-post-content .md-p,
.blog-post-content .md-ul,
.blog-post-content .md-ol,
.blog-post-content .md-blockquote,
.blog-post-content .md-figure,
.blog-post-content .md-pre {
margin: 0.8em 0;
}
.blog-post-content .md-ul,
.blog-post-content .md-ol {
margin-left: 1.35em;
}
.blog-post-content .md-blockquote {
border-left: 3px solid rgba(250, 179, 135, 0.6);
padding: 0.25em 0.9em;
color: rgba(186, 194, 222, 0.86);
font-style: italic;
}
.blog-post-content .md-link,
.blog-index-item-title a {
color: var(--ctp-sky);
text-decoration: none;
}
.blog-post-content .md-link:hover,
.blog-index-item-title a:hover {
color: var(--ctp-blue);
text-decoration: underline;
}
.blog-post-content .md-code {
font-family: "Cascadia Code", "Fira Code", ui-monospace, monospace;
font-size: 0.9em;
background: rgba(49, 50, 68, 0.72);
border-radius: 4px;
padding: 0.15em 0.4em;
color: var(--ctp-sky);
}
.blog-post-content .md-pre {
background: rgba(17, 17, 27, 0.7);
border: 1px solid rgba(205, 214, 244, 0.12);
border-radius: 8px;
padding: 0.8em 0.95em;
overflow: auto;
}
.blog-post-content .md-pre code {
font-family: "Cascadia Code", "Fira Code", ui-monospace, monospace;
color: var(--ctp-sky);
}
.blog-post-content .md-img {
max-width: 100%;
height: auto;
border-radius: 8px;
display: block;
margin: 0 auto;
}
.blog-post-content .md-figcaption {
margin-top: 0.45em;
text-align: center;
color: var(--ctp-subtext0);
font-size: 0.88rem;
}
.blog-index-list {
display: grid;
gap: 12px;
}
.blog-index-item {
background: rgba(24, 24, 37, 0.65);
border: 1px solid rgba(205, 214, 244, 0.12);
border-radius: 12px;
padding: 14px 14px;
}
.blog-index-item-title {
margin: 0;
font-size: 1.05rem;
}
.blog-empty {
color: rgba(186, 194, 222, 0.82);
}
@media (max-width: 820px) {
.blog-page .quick-links {
position: static;
padding: 18px 16px 0;
margin-bottom: -54px;
justify-content: center;
z-index: 1;
}
.blog-shell {
padding-top: 68px;
}
.blog-card {
padding: 18px 16px 22px;
border-radius: 12px;
}
}
/* ─── HUD ─────────────────────────────────────────────────────────────────────── */
.hud {