提交
This commit is contained in:
131
render.js
Normal file
131
render.js
Normal file
@@ -0,0 +1,131 @@
|
||||
import {
|
||||
escapeHtml,
|
||||
escapeAttr,
|
||||
formatDateTime,
|
||||
cleanText,
|
||||
stripMarkdown,
|
||||
b64EncodeUtf8
|
||||
} from "./utils.js";
|
||||
|
||||
const DEFAULT_SITE_NAME_ZH = "萌芽小窝";
|
||||
const DEFAULT_SITE_NAME_EN = "SproutBlog";
|
||||
|
||||
export function siteNameZh(env) {
|
||||
return cleanText(env?.SITE_TITLE) || DEFAULT_SITE_NAME_ZH;
|
||||
}
|
||||
|
||||
export function siteNameEn(env) {
|
||||
return cleanText(env?.SITE_TITLE_EN) || DEFAULT_SITE_NAME_EN;
|
||||
}
|
||||
|
||||
export function pageTitle(env, suffix = "") {
|
||||
const site = siteNameZh(env);
|
||||
return suffix ? `${site} - ${suffix}` : site;
|
||||
}
|
||||
|
||||
export function renderLayout(title, body, env, options = {}) {
|
||||
const siteZh = siteNameZh(env);
|
||||
const siteEn = siteNameEn(env);
|
||||
const cwdPostSlug = options.cwdPostSlug ? String(options.cwdPostSlug) : "";
|
||||
const cwdApiBase =
|
||||
cleanText(env?.CWD_API_BASE) || "https://cwd.api.smyhub.com";
|
||||
const cwdSiteId = cleanText(env?.CWD_SITE_ID);
|
||||
const cwdSection = cwdPostSlug
|
||||
? `
|
||||
<section class="post-comments" aria-labelledby="comments-heading">
|
||||
<h2 id="comments-heading" class="post-comments__title">评论</h2>
|
||||
<div id="comments" data-api-base="${escapeAttr(cwdApiBase)}" data-post-slug="${escapeAttr(cwdPostSlug)}" data-lang="zh-CN"${cwdSiteId ? ` data-site-id="${escapeAttr(cwdSiteId)}"` : ""}></div>
|
||||
</section>`
|
||||
: "";
|
||||
const mdHead = options.markdownPage
|
||||
? `
|
||||
<link rel="stylesheet" href="/css/markdown.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.45/dist/katex.min.css" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11/build/styles/github.min.css" crossorigin="anonymous">
|
||||
`
|
||||
: "";
|
||||
const mdScript = options.markdownPage
|
||||
? `\n <script type="module" src="/js/markdown-client.js"></script>`
|
||||
: "";
|
||||
const cwdScripts = cwdPostSlug
|
||||
? `
|
||||
<script src="https://cdn.jsdelivr.net/npm/cwd-widget@0.1.11/dist/cwd.umd.js" defer></script>
|
||||
<script src="/js/cwd-comments.js" defer></script>`
|
||||
: "";
|
||||
return `<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover">
|
||||
<title>${escapeHtml(title)}</title>
|
||||
<link rel="icon" href="/favicon.ico" sizes="any">
|
||||
<link rel="stylesheet" href="/css/app.css">
|
||||
${mdHead}
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header class="site-header">
|
||||
<div class="site-brand">
|
||||
<img class="site-logo" src="/logo.png" alt="" width="40" height="40" decoding="async">
|
||||
<div class="site-titles">
|
||||
<a href="/" class="site-title-link">${escapeHtml(siteZh)}</a>
|
||||
<span class="site-title-en">${escapeHtml(siteEn)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
${body}
|
||||
${cwdSection}
|
||||
</main>
|
||||
<footer class="site-footer">
|
||||
<p>萌芽小窝-一个安静的小地方 @2026-至今</p>
|
||||
</footer>
|
||||
<div id="admin-gate" class="admin-gate" hidden>
|
||||
<div class="admin-gate-panel">
|
||||
<input id="admin-token" type="password" autocomplete="off" placeholder="Token" aria-label="Token">
|
||||
<button type="button" id="admin-token-go">进入</button>
|
||||
<button type="button" id="admin-token-cancel">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/js/app.js" defer></script>${mdScript}${cwdScripts}
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
export function renderPostCard(post) {
|
||||
const excerpt = post.excerpt || stripMarkdown(post.content).slice(0, 180);
|
||||
const views = Number(post.view_count) || 0;
|
||||
return `
|
||||
<article class="post-card">
|
||||
<h2><a href="/post/${encodeURIComponent(post.slug)}">${escapeHtml(post.title)}</a></h2>
|
||||
<p class="post-excerpt">${escapeHtml(excerpt)}${excerpt.length >= 180 ? "…" : ""}</p>
|
||||
<p class="post-meta">
|
||||
<span class="post-card-time">创建于:${formatDateTime(post.created_at)}</span>
|
||||
<span class="post-card-time">最后更新于:${formatDateTime(post.updated_at)}</span>
|
||||
<span class="post-card-views">浏览 ${views}</span>
|
||||
</p>
|
||||
</article>
|
||||
`;
|
||||
}
|
||||
|
||||
export function renderAdminCard(post) {
|
||||
return `
|
||||
<article>
|
||||
<h3>${escapeHtml(post.title)}</h3>
|
||||
<p>ID: ${post.id} | Slug: ${escapeHtml(post.slug)} | ${post.published ? "已发布" : "草稿"} | ${formatDateTime(post.updated_at)}</p>
|
||||
<div>
|
||||
<a href="/admin/edit/${post.id}">编辑</a>
|
||||
<form method="post" action="/admin/delete" style="display:inline">
|
||||
<input type="hidden" name="id" value="${post.id}">
|
||||
<button type="submit">删除</button>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
`;
|
||||
}
|
||||
|
||||
export function markdownPlaceholder(raw) {
|
||||
const src = String(raw ?? "");
|
||||
if (!src.trim()) return "";
|
||||
const b64 = b64EncodeUtf8(src);
|
||||
return `<div class="markdown-body markdown-pending" data-b64-md="${escapeAttr(b64)}"><p class="markdown-loading">加载中…</p></div>`;
|
||||
}
|
||||
Reference in New Issue
Block a user