mirror of
https://github.com/Ad-closeNN/blog-fuwari.git
synced 2026-05-31 01:20:06 -04:00
feat(theme): 主题相关合并一起、查看源代码按钮
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
---
|
||||
import { type CollectionEntry, render } from "astro:content";
|
||||
import path from "node:path";
|
||||
import { render, type CollectionEntry } from "astro:content";
|
||||
import License from "@components/misc/License.astro";
|
||||
import Markdown from "@components/misc/Markdown.astro";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
@@ -40,9 +40,35 @@ const postSources = import.meta.glob("../../content/posts/**/*.{md,mdx}", {
|
||||
eager: true,
|
||||
}) as Record<string, string>;
|
||||
const normalizedPostSources = Object.fromEntries(
|
||||
Object.entries(postSources).map(([key, source]) => [key.toLowerCase(), source]),
|
||||
Object.entries(postSources).map(([key, source]) => [
|
||||
key.toLowerCase(),
|
||||
source,
|
||||
]),
|
||||
);
|
||||
|
||||
function getPostSourcePath(entryId: string) {
|
||||
const normalizedEntryPath = `../../content/posts/${entryId}`.toLowerCase();
|
||||
const sourceKey = Object.keys(postSources).find((key) => {
|
||||
const normalizedKey = key.toLowerCase();
|
||||
return (
|
||||
normalizedKey === normalizedEntryPath ||
|
||||
normalizedKey === `${normalizedEntryPath}.md` ||
|
||||
normalizedKey === `${normalizedEntryPath}.mdx`
|
||||
);
|
||||
});
|
||||
|
||||
return sourceKey?.replace("../../content/posts/", "src/content/posts/");
|
||||
}
|
||||
|
||||
function getGitHubPostSourceUrl(repo: string | undefined, sourcePath: string | undefined) {
|
||||
if (!repo || !sourcePath) return "";
|
||||
|
||||
const repoUrl = repo.includes("://") ? repo : `https://github.com/${repo}`;
|
||||
const normalizedRepo = repoUrl.replace(/\.git$/, "").replace(/\/+$/, "");
|
||||
const encodedSourcePath = sourcePath.split("/").map(encodeURIComponent).join("/");
|
||||
return `${normalizedRepo}/blob/main/${encodedSourcePath}?plain=1`;
|
||||
}
|
||||
|
||||
function parseAiSummaryValue(value: string) {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return "";
|
||||
@@ -65,7 +91,9 @@ function extractFrontmatterValue(frontmatter: string, key: string) {
|
||||
return parseAiSummaryValue(inlineValue);
|
||||
}
|
||||
|
||||
const afterValue = frontmatter.slice(valueMatch.index! + valueMatch[0].length);
|
||||
const afterValue = frontmatter.slice(
|
||||
valueMatch.index! + valueMatch[0].length,
|
||||
);
|
||||
const lines = afterValue.split(/\r?\n/);
|
||||
const blockLines = [];
|
||||
for (const line of lines) {
|
||||
@@ -82,12 +110,20 @@ function getAiSummaryMeta(entryId: string) {
|
||||
normalizedPostSources[`../../content/posts/${entryId}.mdx`.toLowerCase()];
|
||||
const frontmatter = source?.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1];
|
||||
return {
|
||||
summary: frontmatter ? extractFrontmatterValue(frontmatter, "aiSummary") : "",
|
||||
model: frontmatter ? extractFrontmatterValue(frontmatter, "aiSummaryModel") : "",
|
||||
summary: frontmatter
|
||||
? extractFrontmatterValue(frontmatter, "aiSummary")
|
||||
: "",
|
||||
model: frontmatter
|
||||
? extractFrontmatterValue(frontmatter, "aiSummaryModel")
|
||||
: "",
|
||||
};
|
||||
}
|
||||
|
||||
const aiSummaryMeta = getAiSummaryMeta(entry.id);
|
||||
const postSourceUrl = getGitHubPostSourceUrl(
|
||||
siteConfig.githubRepo,
|
||||
getPostSourcePath(entry.id),
|
||||
);
|
||||
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
@@ -113,7 +149,6 @@ const showcover = entry.data.showcover;
|
||||
// 获取自定义的头图(Markdown 内部)
|
||||
const customcover = entry.data.customcover;
|
||||
const isOutdated = entry.data.outdated;
|
||||
|
||||
---
|
||||
<MainGridLayout banner={entry.data.image} title={entry.data.title} description={entry.data.description} lang={entry.data.lang} setOGTypeArticle={true} headings={headings}>
|
||||
<script is:inline slot="head" type="application/ld+json" set:html={JSON.stringify(jsonLd)}></script>
|
||||
@@ -126,13 +161,13 @@ const isOutdated = entry.data.outdated;
|
||||
<div class="flex flex-row flex-wrap text-black/30 dark:text-white/30 gap-5 transition">
|
||||
<div class="flex flex-row items-center pointer-events-none select-none cursor-default">
|
||||
<div class="transition h-6 w-6 rounded-md bg-black/5 dark:bg-white/10 text-black/50 dark:text-white/50 flex items-center justify-center mr-2">
|
||||
<Icon name="material-symbols:notes-rounded"></Icon>
|
||||
<Icon is:inline name="material-symbols:notes-rounded"></Icon>
|
||||
</div>
|
||||
<div class="text-sm">{remarkPluginFrontmatter.words} {" " + i18n(I18nKey.wordsCount)}</div>
|
||||
</div>
|
||||
<div class="flex flex-row items-center pointer-events-none select-none cursor-default">
|
||||
<div class="transition h-6 w-6 rounded-md bg-black/5 dark:bg-white/10 text-black/50 dark:text-white/50 flex items-center justify-center mr-2">
|
||||
<Icon name="material-symbols:schedule-outline-rounded"></Icon>
|
||||
<Icon is:inline name="material-symbols:schedule-outline-rounded"></Icon>
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
大约 {remarkPluginFrontmatter.minutes} {" " + i18n(remarkPluginFrontmatter.minutes === 1 ? I18nKey.minuteCount : I18nKey.minutesCount)}
|
||||
@@ -140,12 +175,25 @@ const isOutdated = entry.data.outdated;
|
||||
</div>
|
||||
<div class="flex flex-row items-center pointer-events-none select-none cursor-default">
|
||||
<div class="transition h-6 w-6 rounded-md bg-black/5 dark:bg-white/10 text-black/50 dark:text-white/50 flex items-center justify-center mr-2">
|
||||
<Icon name="material-symbols:visibility-outline-rounded"></Icon>
|
||||
<Icon is:inline name="material-symbols:visibility-outline-rounded"></Icon>
|
||||
</div>
|
||||
<div class="text-sm" id="post-top-page-views">加载中...</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-end gap-2 text-black/40 dark:text-white/40 md:shrink-0">
|
||||
{postSourceUrl && (
|
||||
<a
|
||||
href={postSourceUrl}
|
||||
aria-label="查看文章源代码"
|
||||
title="查看文章源代码"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn-card flex h-9 items-center gap-2 rounded-xl px-3 font-medium active:scale-95"
|
||||
>
|
||||
<Icon is:inline name="fa6-brands:github" class="text-[1rem] text-[var(--primary)]"></Icon>
|
||||
<span class="hidden text-sm text-black/75 dark:text-white/75 sm:inline">查看源代码</span>
|
||||
</a>
|
||||
)}
|
||||
<a
|
||||
href={entry.data.nextSlug ? getPostUrlBySlug(entry.data.nextSlug) : "#"}
|
||||
aria-label={entry.data.nextTitle ? `上一篇:${entry.data.nextTitle}` : "没有上一篇"}
|
||||
@@ -155,7 +203,7 @@ const isOutdated = entry.data.outdated;
|
||||
{"pointer-events-none opacity-40": !entry.data.nextSlug},
|
||||
]}
|
||||
>
|
||||
<Icon name="material-symbols:chevron-left-rounded" class="text-[1.5rem] text-[var(--primary)]"></Icon>
|
||||
<Icon is:inline name="material-symbols:chevron-left-rounded" class="text-[1.5rem] text-[var(--primary)]"></Icon>
|
||||
</a>
|
||||
<a
|
||||
href={entry.data.prevSlug ? getPostUrlBySlug(entry.data.prevSlug) : "#"}
|
||||
@@ -166,7 +214,7 @@ const isOutdated = entry.data.outdated;
|
||||
{"pointer-events-none opacity-40": !entry.data.prevSlug},
|
||||
]}
|
||||
>
|
||||
<Icon name="material-symbols:chevron-right-rounded" class="text-[1.5rem] text-[var(--primary)]"></Icon>
|
||||
<Icon is:inline name="material-symbols:chevron-right-rounded" class="text-[1.5rem] text-[var(--primary)]"></Icon>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -204,6 +252,7 @@ const isOutdated = entry.data.outdated;
|
||||
]}>
|
||||
<div class="flex items-start gap-2">
|
||||
<Icon
|
||||
is:inline
|
||||
name={isOutdated ? "material-symbols:warning-outline-rounded" : "material-symbols:info-outline-rounded"}
|
||||
class="mt-0.5 shrink-0 text-lg"
|
||||
></Icon>
|
||||
@@ -236,7 +285,7 @@ const isOutdated = entry.data.outdated;
|
||||
<div class="mb-4 rounded-xl p-4 bg-gradient-to-r from-violet-500/10 to-indigo-500/10 dark:from-violet-500/15 dark:to-indigo-500/15 border border-violet-500/20 onload-animation">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="shrink-0 w-8 h-8 rounded-lg bg-violet-500/20 dark:bg-violet-500/30 flex items-center justify-center">
|
||||
<Icon name="material-symbols:info-outline-rounded" class="text-violet-600 dark:text-violet-400 text-lg"></Icon>
|
||||
<Icon is:inline name="material-symbols:info-outline-rounded" class="text-violet-600 dark:text-violet-400 text-lg"></Icon>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="text-sm font-medium text-violet-700 dark:text-violet-300 mb-1 select-none pointer-events-none">
|
||||
@@ -279,7 +328,7 @@ const isOutdated = entry.data.outdated;
|
||||
<a href={entry.data.nextSlug ? getPostUrlBySlug(entry.data.nextSlug) : "#"}
|
||||
class:list={["w-full font-bold overflow-hidden active:scale-95", {"pointer-events-none": !entry.data.nextSlug}]}>
|
||||
{entry.data.nextSlug && <div class="btn-card rounded-2xl w-full h-[3.75rem] max-w-full px-4 flex items-center !justify-start gap-4" >
|
||||
<Icon name="material-symbols:chevron-left-rounded" class="text-[2rem] text-[var(--primary)]" />
|
||||
<Icon is:inline name="material-symbols:chevron-left-rounded" class="text-[2rem] text-[var(--primary)]" />
|
||||
<div class="overflow-hidden transition overflow-ellipsis whitespace-nowrap max-w-[calc(100%_-_3rem)] text-black/75 dark:text-white/75">
|
||||
{entry.data.nextTitle}
|
||||
</div>
|
||||
@@ -292,7 +341,7 @@ const isOutdated = entry.data.outdated;
|
||||
<div class="overflow-hidden transition overflow-ellipsis whitespace-nowrap max-w-[calc(100%_-_3rem)] text-black/75 dark:text-white/75">
|
||||
{entry.data.prevTitle}
|
||||
</div>
|
||||
<Icon name="material-symbols:chevron-right-rounded" class="text-[2rem] text-[var(--primary)]" />
|
||||
<Icon is:inline name="material-symbols:chevron-right-rounded" class="text-[2rem] text-[var(--primary)]" />
|
||||
</div>}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user