671 lines
18 KiB
Plaintext
671 lines
18 KiB
Plaintext
<%- js('js/fancybox.js') %>
|
||
|
||
<style>
|
||
.post-container {
|
||
max-width: 900px;
|
||
margin: 0 auto;
|
||
padding: 40px 20px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 24px;
|
||
}
|
||
|
||
.post-card-wide {
|
||
border-radius: 20px;
|
||
background-color: rgb(var(--mdui-color-surface-container-low));
|
||
box-shadow: none;
|
||
}
|
||
|
||
.post-card-comment {
|
||
border-radius: 20px;
|
||
background-color: rgb(var(--mdui-color-surface-container-low));
|
||
box-shadow: none;
|
||
padding: 2.5rem;
|
||
}
|
||
|
||
.post-card-nav {
|
||
border-radius: 20px;
|
||
background-color: rgb(var(--mdui-color-surface-container-low));
|
||
box-shadow: none;
|
||
padding: 2.5rem;
|
||
}
|
||
|
||
.markdown-section img,
|
||
.article-content img {
|
||
max-width: 100%;
|
||
height: auto;
|
||
display: block;
|
||
margin: 1.2em auto;
|
||
object-fit: contain;
|
||
box-sizing: border-box;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.caption {
|
||
color: #444;
|
||
display: block;
|
||
font-size: 0.9em;
|
||
margin-top: 0.1em;
|
||
position: relative;
|
||
text-align: center;
|
||
}
|
||
|
||
.article-content {
|
||
color: rgb(var(--mdui-color-on-surface));
|
||
line-height: 1.8;
|
||
font-family: var(--mdui-font-family);
|
||
width: 100%;
|
||
overflow-wrap: break-word;
|
||
}
|
||
|
||
.article-content h1,
|
||
.article-content h2,
|
||
.article-content h3,
|
||
.article-content h4,
|
||
.article-content h5,
|
||
.article-content h6 {
|
||
color: rgb(var(--mdui-color-primary));
|
||
margin-top: 1.5em;
|
||
margin-bottom: 1em;
|
||
}
|
||
|
||
.article-content p {
|
||
margin-bottom: 1.2em;
|
||
}
|
||
|
||
.article-content a {
|
||
color: rgb(var(--mdui-color-primary));
|
||
text-decoration: none;
|
||
border-bottom: 1px dashed rgb(var(--mdui-color-primary));
|
||
}
|
||
|
||
.article-content a:hover {
|
||
opacity: 0.8;
|
||
}
|
||
|
||
.article-content code {
|
||
background-color: rgb(var(--mdui-color-surface-container-high));
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-family: var(--mdui-font-family-mono);
|
||
word-break: break-word;
|
||
}
|
||
|
||
/* 修复代码块样式 - 关键修复 */
|
||
.mdui-code-block {
|
||
background: rgb(var(--mdui-color-surface-container-high));
|
||
border-radius: 12px;
|
||
margin: 1.5em 0;
|
||
overflow: hidden;
|
||
font-family: var(--mdui-font-family-mono);
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
/* 确保代码块不会超出容器 */
|
||
max-width: 100%;
|
||
}
|
||
|
||
.mdui-code-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 16px;
|
||
background: rgb(var(--mdui-color-surface-container-highest));
|
||
border-bottom: 1px solid rgb(var(--mdui-color-outline-variant));
|
||
position: relative;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
}
|
||
|
||
.mdui-code-language {
|
||
color: rgb(var(--mdui-color-on-surface-variant));
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
font-family: var(--mdui-font-family);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.mdui-code-actions {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.mdui-code-toggle {
|
||
color: rgb(var(--mdui-color-on-surface-variant));
|
||
cursor: pointer;
|
||
transition: color 0.2s ease;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.mdui-code-toggle:hover {
|
||
color: rgb(var(--mdui-color-primary));
|
||
}
|
||
|
||
.mdui-code-copy {
|
||
color: rgb(var(--mdui-color-on-surface-variant));
|
||
cursor: pointer;
|
||
transition: color 0.2s ease;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.mdui-code-copy:hover {
|
||
color: rgb(var(--mdui-color-primary));
|
||
}
|
||
|
||
.mdui-code-copy.copied {
|
||
color: rgb(var(--mdui-color-success));
|
||
}
|
||
|
||
/* 关键修复:代码内容区域 */
|
||
.mdui-code-content {
|
||
position: relative;
|
||
background: rgb(var(--mdui-color-surface-container-high));
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
/* 修复滚动问题 */
|
||
overflow: hidden;
|
||
-webkit-overflow-scrolling: touch;
|
||
/* 使用 flex 布局:左侧行号列 + 右侧代码内容 */
|
||
display: flex;
|
||
align-items: stretch;
|
||
}
|
||
|
||
/* 关键修复:代码内容本身 */
|
||
.mdui-code-pre {
|
||
margin: 0;
|
||
padding: 16px;
|
||
font-family: var(--mdui-font-family-mono);
|
||
font-size: 0.875rem;
|
||
line-height: 1.5;
|
||
color: rgb(var(--mdui-color-on-surface));
|
||
/* 关键:保持代码不换行 */
|
||
white-space: pre;
|
||
/* 关键:让内容自然扩展 */
|
||
display: block;
|
||
box-sizing: border-box;
|
||
/* 允许横向滚动,但代码区域使用 flex:1 */
|
||
flex: 1 1 auto;
|
||
min-width: 0; /* allow flex to shrink */
|
||
}
|
||
|
||
/* 行号 gutter */
|
||
.mdui-code-gutter {
|
||
padding: 12px 8px;
|
||
background: rgb(var(--mdui-color-surface-container));
|
||
color: rgb(var(--mdui-color-on-surface-variant));
|
||
text-align: right;
|
||
user-select: none;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0;
|
||
flex: 0 0 auto;
|
||
}
|
||
|
||
.mdui-code-gutter .mdui-code-line-number {
|
||
display: block;
|
||
font-size: 0.8125rem;
|
||
line-height: 1.5;
|
||
min-height: 1.5em;
|
||
padding: 0 4px;
|
||
}
|
||
|
||
/* 自定义滚动条样式 */
|
||
.mdui-code-content::-webkit-scrollbar {
|
||
height: 8px;
|
||
}
|
||
|
||
.mdui-code-content::-webkit-scrollbar-track {
|
||
background: rgb(var(--mdui-color-surface-container));
|
||
border-radius: 0 0 4px 4px;
|
||
}
|
||
|
||
.mdui-code-content::-webkit-scrollbar-thumb {
|
||
background: rgb(var(--mdui-color-outline-variant));
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.mdui-code-content::-webkit-scrollbar-thumb:hover {
|
||
background: rgb(var(--mdui-color-outline));
|
||
}
|
||
|
||
/* Firefox 滚动条样式 */
|
||
.mdui-code-content {
|
||
scrollbar-width: thin;
|
||
scrollbar-color: rgb(var(--mdui-color-outline-variant)) rgb(var(--mdui-color-surface-container));
|
||
}
|
||
|
||
.mdui-code-block.collapsed .mdui-code-content {
|
||
display: none;
|
||
}
|
||
|
||
.mdui-code-block.collapsed .mdui-code-toggle .expand-icon {
|
||
display: inline;
|
||
}
|
||
|
||
.mdui-code-block.collapsed .mdui-code-toggle .collapse-icon {
|
||
display: none;
|
||
}
|
||
|
||
.mdui-code-block:not(.collapsed) .mdui-code-toggle .expand-icon {
|
||
display: none;
|
||
}
|
||
|
||
.mdui-code-block:not(.collapsed) .mdui-code-toggle .collapse-icon {
|
||
display: inline;
|
||
}
|
||
|
||
.article-content pre {
|
||
background-color: rgb(var(--mdui-color-surface-container-high));
|
||
padding: 16px;
|
||
border-radius: 8px;
|
||
overflow-x: auto;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.article-content blockquote {
|
||
border-left: 4px solid rgb(var(--mdui-color-primary));
|
||
padding: 8px 16px;
|
||
margin: 16px 0;
|
||
background-color: rgb(var(--mdui-color-surface-container));
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.article-content ul,
|
||
.article-content ol {
|
||
padding-left: 32px;
|
||
margin-bottom: 16px;
|
||
width: 100%;
|
||
}
|
||
|
||
.article-content li {
|
||
margin-bottom: 8px;
|
||
word-break: break-word;
|
||
}
|
||
|
||
.post-navigation {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
padding: 1rem 0;
|
||
}
|
||
|
||
.nav-button {
|
||
flex: 1;
|
||
text-align: center;
|
||
padding: 16px;
|
||
border-radius: 12px;
|
||
background-color: rgb(var(--mdui-color-surface-container));
|
||
color: rgb(var(--mdui-color-on-surface));
|
||
text-decoration: none;
|
||
transition: all 0.3s ease;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.nav-button:hover {
|
||
background-color: rgb(var(--mdui-color-surface-container-high));
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.nav-button.disabled {
|
||
opacity: 0.5;
|
||
cursor: not-allowed;
|
||
transform: none;
|
||
}
|
||
|
||
.nav-button.disabled:hover {
|
||
background-color: rgb(var(--mdui-color-surface-container));
|
||
transform: none;
|
||
}
|
||
|
||
.nav-label {
|
||
font-size: 0.875rem;
|
||
color: rgb(var(--mdui-color-on-surface-variant));
|
||
}
|
||
|
||
.nav-title {
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* 平板设备 */
|
||
@media (max-width: 1024px) {
|
||
.post-container {
|
||
padding: 2rem 24px;
|
||
gap: 20px;
|
||
}
|
||
|
||
.post-card-wide,
|
||
.post-card-comment,
|
||
.post-card-nav {
|
||
padding: 2rem;
|
||
}
|
||
}
|
||
|
||
/* 手机设备 */
|
||
@media (max-width: 768px) {
|
||
.post-container {
|
||
padding: 24px 16px;
|
||
gap: 16px;
|
||
}
|
||
|
||
.post-card-wide {
|
||
padding: 1.5rem;
|
||
}
|
||
|
||
.post-card-comment,
|
||
.post-card-nav {
|
||
padding: 1.5rem;
|
||
}
|
||
|
||
.post-navigation {
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.mdui-code-header {
|
||
padding: 10px 12px;
|
||
}
|
||
|
||
.mdui-code-pre {
|
||
padding: 12px;
|
||
font-size: 0.8125rem;
|
||
}
|
||
|
||
.mdui-code-content::-webkit-scrollbar {
|
||
height: 6px;
|
||
}
|
||
|
||
/* 移动端代码块特殊处理 */
|
||
.mdui-code-block {
|
||
margin: 1em 0;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
.article-content {
|
||
font-size: 0.9375rem;
|
||
}
|
||
}
|
||
|
||
/* 小屏手机 */
|
||
@media (max-width: 480px) {
|
||
.post-container {
|
||
padding: 20px 12px;
|
||
gap: 12px;
|
||
}
|
||
|
||
.post-card-wide,
|
||
.post-card-comment,
|
||
.post-card-nav {
|
||
padding: 1rem;
|
||
border-radius: 16px;
|
||
}
|
||
|
||
.mdui-code-pre {
|
||
padding: 10px;
|
||
font-size: 0.75rem;
|
||
}
|
||
|
||
.article-content ul,
|
||
.article-content ol {
|
||
padding-left: 24px;
|
||
}
|
||
|
||
.mdui-code-header {
|
||
padding: 8px 12px;
|
||
}
|
||
|
||
.mdui-code-language {
|
||
font-size: 0.75rem;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<mdui-layout-main>
|
||
<%- include('widgets/banner', {titleText1: page.title, titleText2: ''}) %>
|
||
|
||
<div class="post-container">
|
||
<!-- 内容卡片 -->
|
||
<mdui-card variant="filled" class="post-card-wide">
|
||
<mdui-card-content>
|
||
<div class="article-content">
|
||
<%- page.content %>
|
||
</div>
|
||
</mdui-card-content>
|
||
</mdui-card>
|
||
|
||
<!-- 上一篇/下一篇导航 -->
|
||
<mdui-card variant="filled" class="post-card-nav">
|
||
<mdui-card-content>
|
||
<div class="post-navigation">
|
||
<% if (page.prev) { %>
|
||
<a href="<%= url_for(page.prev.path) %>" class="nav-button">
|
||
<span class="nav-label"><%= __('post.prev') %></span>
|
||
<span class="nav-title"><%= page.prev.title %></span>
|
||
</a>
|
||
<% } else { %>
|
||
<a class="nav-button disabled">
|
||
<span class="nav-label"><%= __('post.prev') %></span>
|
||
<span class="nav-title"><%= __('post.none') %></span>
|
||
</a>
|
||
<% } %>
|
||
|
||
<% if (page.next) { %>
|
||
<a href="<%= url_for(page.next.path) %>" class="nav-button">
|
||
<span class="nav-label"><%= __('post.next') %></span>
|
||
<span class="nav-title"><%= page.next.title %></span>
|
||
</a>
|
||
<% } else { %>
|
||
<a class="nav-button disabled">
|
||
<span class="nav-label"><%= __('post.next') %></span>
|
||
<span class="nav-title"><%= __('post.none') %></span>
|
||
</a>
|
||
<% } %>
|
||
</div>
|
||
</mdui-card-content>
|
||
</mdui-card>
|
||
|
||
<!-- 评论卡片 -->
|
||
<mdui-card variant="filled" class="post-card-comment">
|
||
<mdui-card-content>
|
||
<mdui-typography variant="h5" style="margin-bottom: 1.5rem; text-align: center; color: rgb(var(--mdui-color-primary));">
|
||
<%= __('post.comment') %>
|
||
</mdui-typography>
|
||
<% if (theme.comment && theme.comment.service) { %>
|
||
<% if (theme.comment.service === 'twikoo') { %>
|
||
<%- include('widgets/comments/twikoo.ejs') %>
|
||
<% } else if (theme.comment.service === 'waline') { %>
|
||
<%- include('widgets/comments/waline.ejs') %>
|
||
<% } else if (theme.comment.service === 'giscus') { %>
|
||
<%- include('widgets/comments/giscus.ejs') %>
|
||
<% } else { %>
|
||
<div style="text-align: center; padding: 2rem;">
|
||
<mdui-typography variant="body2" style="color: var(--mdui-color-on-surface-variant);">
|
||
<%= __('post.comment_disabled') %>
|
||
</mdui-typography>
|
||
</div>
|
||
<% } %>
|
||
<% } else { %>
|
||
<div style="text-align: center; padding: 2rem;">
|
||
<mdui-typography variant="body2" style="color: var(--mdui-color-on-surface-variant);">
|
||
<%= __('post.comment_disabled') %>
|
||
</mdui-typography>
|
||
</div>
|
||
<% } %>
|
||
</mdui-card-content>
|
||
</mdui-card>
|
||
</div>
|
||
</mdui-layout-main>
|
||
|
||
<script>
|
||
// 代码块功能增强(重构版)
|
||
(function(){
|
||
function detectLanguage(pre){
|
||
// 优先考虑 data-language 属性
|
||
if (pre.dataset && pre.dataset.language) return pre.dataset.language;
|
||
const code = pre.querySelector('code');
|
||
const cls = code && code.className ? code.className : '';
|
||
const m = cls.match(/language-([A-Za-z0-9_\-]+)/) || cls.match(/lang-([A-Za-z0-9_\-]+)/);
|
||
if (m) return m[1];
|
||
return 'Code';
|
||
}
|
||
|
||
function makeHeader(language){
|
||
const header = document.createElement('div');
|
||
header.className = 'mdui-code-header';
|
||
|
||
const langSpan = document.createElement('span');
|
||
langSpan.className = 'mdui-code-language';
|
||
langSpan.textContent = language;
|
||
|
||
const actions = document.createElement('div');
|
||
actions.className = 'mdui-code-actions';
|
||
|
||
const toggle = document.createElement('button');
|
||
toggle.type = 'button';
|
||
toggle.className = 'mdui-code-toggle';
|
||
toggle.setAttribute('aria-label', 'Toggle code');
|
||
toggle.innerHTML = '<mdui-icon class="collapse-icon" size="small">unfold_less</mdui-icon><mdui-icon class="expand-icon" size="small">unfold_more</mdui-icon>';
|
||
|
||
const copy = document.createElement('button');
|
||
copy.type = 'button';
|
||
copy.className = 'mdui-code-copy';
|
||
copy.setAttribute('aria-label', 'Copy code');
|
||
copy.innerHTML = '<mdui-icon size="small">content_copy</mdui-icon>';
|
||
|
||
actions.appendChild(toggle);
|
||
actions.appendChild(copy);
|
||
|
||
header.appendChild(langSpan);
|
||
header.appendChild(actions);
|
||
return header;
|
||
}
|
||
|
||
function processPre(pre){
|
||
if (pre.closest('.mdui-code-block')) return;
|
||
|
||
const language = detectLanguage(pre);
|
||
|
||
const codeBlock = document.createElement('div');
|
||
codeBlock.className = 'mdui-code-block';
|
||
|
||
const header = makeHeader(language);
|
||
|
||
const content = document.createElement('div');
|
||
content.className = 'mdui-code-content';
|
||
|
||
// 移动原始 pre 元素到新的容器,保留任何高亮 markup
|
||
pre.classList.add('mdui-code-pre');
|
||
|
||
// 生成行号 gutter(不包含在复制内容中)
|
||
try {
|
||
const codeText = pre.textContent || pre.innerText || '';
|
||
const lines = codeText.split(/\r\n|\r|\n/);
|
||
const gutter = document.createElement('div');
|
||
gutter.className = 'mdui-code-gutter';
|
||
for (let i = 0; i < lines.length; i++) {
|
||
const ln = document.createElement('span');
|
||
ln.className = 'mdui-code-line-number';
|
||
ln.textContent = (i + 1);
|
||
gutter.appendChild(ln);
|
||
}
|
||
content.appendChild(gutter);
|
||
} catch (e) {
|
||
// ignore gutter errors
|
||
}
|
||
|
||
content.appendChild(pre);
|
||
|
||
codeBlock.appendChild(header);
|
||
codeBlock.appendChild(content);
|
||
|
||
// 替换原位置(pre 将被移动到新的 content 中)
|
||
if (pre.parentNode) {
|
||
pre.parentNode.replaceChild(codeBlock, pre);
|
||
} else {
|
||
// 保险回退:尝试查找原来应该替换的位置
|
||
const sibling = document.querySelector('body');
|
||
// 最后手段:附加到 body(极少出现)
|
||
sibling && sibling.appendChild(codeBlock);
|
||
}
|
||
}
|
||
|
||
function init(){
|
||
document.querySelectorAll('pre').forEach(processPre);
|
||
|
||
// 事件委托:折叠/展开 & 复制
|
||
document.addEventListener('click', function(e){
|
||
const toggle = e.target.closest('.mdui-code-toggle');
|
||
if (toggle) {
|
||
const block = toggle.closest('.mdui-code-block');
|
||
block && block.classList.toggle('collapsed');
|
||
return;
|
||
}
|
||
|
||
const copyBtn = e.target.closest('.mdui-code-copy');
|
||
if (copyBtn) {
|
||
const block = copyBtn.closest('.mdui-code-block');
|
||
if (!block) return;
|
||
const pre = block.querySelector('.mdui-code-pre');
|
||
const codeText = pre ? pre.textContent : '';
|
||
|
||
// 复制并给出视觉反馈
|
||
navigator.clipboard && navigator.clipboard.writeText
|
||
? navigator.clipboard.writeText(codeText).then(() => flashCopy(copyBtn))
|
||
: fallbackCopy(codeText, copyBtn);
|
||
}
|
||
});
|
||
|
||
// 初始移动端修复
|
||
function fixCodeBlocksOnMobile(){
|
||
document.querySelectorAll('.mdui-code-block').forEach(block => {
|
||
const content = block.querySelector('.mdui-code-content');
|
||
const pre = block.querySelector('.mdui-code-pre');
|
||
if (content && pre) {
|
||
content.style.overflowX = 'auto';
|
||
pre.style.whiteSpace = 'pre';
|
||
pre.style.display = 'inline-block';
|
||
pre.style.minWidth = 'min-content';
|
||
}
|
||
});
|
||
}
|
||
|
||
function flashCopy(button){
|
||
const original = button.innerHTML;
|
||
button.innerHTML = '<mdui-icon size="small" style="color: var(--mdui-color-success)">check</mdui-icon>';
|
||
button.classList.add('copied');
|
||
setTimeout(()=>{ button.innerHTML = original; button.classList.remove('copied'); }, 2000);
|
||
}
|
||
|
||
function fallbackCopy(text, button){
|
||
try {
|
||
const ta = document.createElement('textarea');
|
||
ta.value = text;
|
||
ta.style.position = 'fixed'; ta.style.left = '-9999px';
|
||
document.body.appendChild(ta);
|
||
ta.select();
|
||
document.execCommand('copy');
|
||
document.body.removeChild(ta);
|
||
flashCopy(button);
|
||
} catch (err) {
|
||
console.error('Fallback copy failed', err);
|
||
}
|
||
}
|
||
|
||
fixCodeBlocksOnMobile();
|
||
window.addEventListener('resize', fixCodeBlocksOnMobile);
|
||
}
|
||
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
} else {
|
||
init();
|
||
}
|
||
})();
|
||
</script> |