- Migrate historical posts, localized assets, and 103 comments - Colocate media into posts/<slug>/images/ and remove wp-content - Add native bilingual support for ai-can-change post (English default, /zh variant) - Support clean taxonomy routing (/category/:category and /tag/:tag) - Dynamically populate categories and tags in sitemap.xml - Fix header scroll padding/margin offsets for anchor and reference links - Constrain image and media widths to prevent content overflow - Redesign homepage recent posts to 4 compact editorial cards - Restore valid binary favicon.ico and high-res icon assets - Remove placeholder content and sample test comments
140 lines
5.5 KiB
Plaintext
140 lines
5.5 KiB
Plaintext
<%- include('../partials/head', { pageType: 'post' }) %>
|
|
|
|
<div class="post-layout-container">
|
|
<article class="post-article-main">
|
|
<header class="post-header-editorial">
|
|
<h1 class="post-title-editorial"><%= post.title %></h1>
|
|
|
|
<div class="post-meta-editorial">
|
|
<time datetime="<%= post.date.toISOString() %>"><%= post.date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) %></time>
|
|
<span class="meta-sep">—</span>
|
|
<span>by <%= post.author %></span>
|
|
<% if (post.category) { %>
|
|
<span>in <a href="/category/<%= encodeURIComponent(post.category) %>" class="meta-category-link"><%= post.category %></a></span>
|
|
<% } %>
|
|
|
|
<!-- Inline Bilingual Language Switcher with stable line height -->
|
|
<% if (post.availableLanguages && post.availableLanguages.length > 1) { %>
|
|
<span class="meta-sep">•</span>
|
|
<span class="meta-lang-switcher">
|
|
<span style="opacity: 0.7;">🌐</span>
|
|
<% post.availableLanguages.forEach(l => {
|
|
const isCurrent = (currentLang === l);
|
|
const targetUrl = (l === post.defaultLang || (post.isDefault && l === post.lang))
|
|
? `/${post.slug}`
|
|
: `/${post.slug}/${l}`;
|
|
const displayLabel = l === 'zh' ? '中文' : (l === 'en' ? 'English' : l.toUpperCase());
|
|
%>
|
|
<a href="<%= targetUrl %>" class="lang-text-link <%= isCurrent ? 'lang-active' : '' %>">
|
|
<%= displayLabel %>
|
|
</a>
|
|
<% }) %>
|
|
</span>
|
|
<% } %>
|
|
</div>
|
|
</header>
|
|
|
|
<% if (post.cover) { %>
|
|
<img src="<%= post.cover %>" alt="<%= post.title %>" class="post-featured-image">
|
|
<% } %>
|
|
|
|
<!-- Rendered Markdown Body -->
|
|
<div class="post-body-content">
|
|
<%- post.contentHtml %>
|
|
</div>
|
|
|
|
<!-- Unified Tag Links -->
|
|
<% if (post.tags && post.tags.length > 0) { %>
|
|
<div class="post-tags-container">
|
|
<span class="post-tags-label">Tags:</span>
|
|
<% post.tags.forEach(tag => { %>
|
|
<a href="/tag/<%= encodeURIComponent(tag.toLowerCase()) %>" class="tag-link">
|
|
#<%= tag %>
|
|
</a>
|
|
<% }) %>
|
|
</div>
|
|
<% } %>
|
|
|
|
<!-- Plugin Hook: injectPostEnd -->
|
|
<%- pluginHook('injectPostEnd', { post, pageType: 'post' }) %>
|
|
|
|
<!-- Clean Previous / Next Post Navigation -->
|
|
<% if (prevPost || nextPost) { %>
|
|
<nav class="post-nav-editorial">
|
|
<div class="nav-prev">
|
|
<% if (prevPost) { %>
|
|
<a href="/<%= prevPost.slug %>" class="editorial-nav-link">
|
|
← Previous: <%= prevPost.title %>
|
|
</a>
|
|
<% } %>
|
|
</div>
|
|
|
|
<div class="nav-next">
|
|
<% if (nextPost) { %>
|
|
<a href="/<%= nextPost.slug %>" class="editorial-nav-link">
|
|
Next: <%= nextPost.title %> →
|
|
</a>
|
|
<% } %>
|
|
</div>
|
|
</nav>
|
|
<% } %>
|
|
|
|
<!-- Modernized Comments Section -->
|
|
<%- include('../partials/comments', { post, comments, commentStatus }) %>
|
|
</article>
|
|
|
|
<!-- Ambient Floating Side Rail TOC (Positioned in left margin, Dropbox Paper style) -->
|
|
<% if (post.toc && post.toc.length > 0) { %>
|
|
<aside class="desktop-side-toc" id="side-toc">
|
|
<div class="side-toc-sticky">
|
|
<div class="side-toc-header">
|
|
<span class="side-toc-title">Contents</span>
|
|
<button type="button" class="side-toc-minimize-btn" id="side-toc-minimize" title="Minimize Table of Contents" aria-label="Minimize Table of Contents">—</button>
|
|
</div>
|
|
<nav class="side-toc-nav" id="side-toc-nav">
|
|
<ul class="side-toc-list">
|
|
<% post.toc.forEach(h => { %>
|
|
<li class="side-toc-item level-<%= h.level %>">
|
|
<a href="#<%= h.id %>" class="side-toc-link" data-id="<%= h.id %>">
|
|
<span class="toc-rail-indicator"></span>
|
|
<span class="toc-link-text"><%= h.title %></span>
|
|
</a>
|
|
</li>
|
|
<% }) %>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- Floating Dock Badge (shown when side TOC is minimized, or on compact/mobile screens) -->
|
|
<button type="button" class="floating-toc-badge" id="floating-toc-badge" aria-label="Open Table of Contents" title="Table of Contents">
|
|
<span class="badge-icon">📑</span>
|
|
<span class="badge-label">Contents</span>
|
|
<span class="badge-count"><%= post.toc.length %></span>
|
|
</button>
|
|
|
|
<!-- Mobile Slide-Up Drawer Sheet & Dark Backdrop -->
|
|
<div class="mobile-toc-overlay" id="mobile-toc-overlay"></div>
|
|
<div class="mobile-toc-sheet" id="mobile-toc-sheet">
|
|
<div class="mobile-sheet-header">
|
|
<div class="mobile-sheet-title">
|
|
<span>📑 Table of Contents</span>
|
|
<span class="pill" style="font-size: 0.75rem; background: var(--color-surface); border: 1px solid var(--color-border);"><%= post.toc.length %> sections</span>
|
|
</div>
|
|
<button type="button" class="mobile-sheet-close" id="mobile-sheet-close" aria-label="Close Table of Contents">×</button>
|
|
</div>
|
|
<nav class="mobile-sheet-body">
|
|
<ol class="mobile-sheet-list">
|
|
<% post.toc.forEach(h => { %>
|
|
<li class="mobile-sheet-item level-<%= h.level %>">
|
|
<a href="#<%= h.id %>" class="mobile-sheet-link"><%= h.title %></a>
|
|
</li>
|
|
<% }) %>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<% } %>
|
|
</div>
|
|
|
|
<%- include('../partials/foot', { pageType: 'post' }) %>
|