switched to astro
This commit is contained in:
parent
9c8264f186
commit
46daf98523
196 changed files with 14792 additions and 19285 deletions
24
src/pages/[...page].astro
Normal file
24
src/pages/[...page].astro
Normal file
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
import MainGridLayout from "../layouts/MainGridLayout.astro";
|
||||
import PostCard from "../components/PostCard.astro";
|
||||
import Pagination from "../components/control/Pagination.astro";
|
||||
import {getSortedPosts} from "../utils/content-utils";
|
||||
import {getPostUrlBySlug} from "../utils/url-utils";
|
||||
import {PAGE_SIZE} from "../constants/constants";
|
||||
import PostPage from "../components/PostPage.astro";
|
||||
|
||||
export async function getStaticPaths({ paginate }) {
|
||||
const allBlogPosts = await getSortedPosts();
|
||||
return paginate(allBlogPosts, { pageSize: PAGE_SIZE });
|
||||
}
|
||||
|
||||
const {page} = Astro.props;
|
||||
|
||||
const len = page.data.length;
|
||||
|
||||
---
|
||||
|
||||
<MainGridLayout>
|
||||
<PostPage page={page}></PostPage>
|
||||
<Pagination class="mx-auto onload-animation" page={page} style=`animation-delay: calc(var(--content-delay) + ${(len)*50}ms)`></Pagination>
|
||||
</MainGridLayout>
|
23
src/pages/about.astro
Normal file
23
src/pages/about.astro
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
|
||||
import MainGridLayout from "../layouts/MainGridLayout.astro";
|
||||
|
||||
import { getEntry } from 'astro:content'
|
||||
import {i18n} from "../i18n/translation";
|
||||
import I18nKey from "../i18n/i18nKey";
|
||||
import Markdown from "@components/misc/Markdown.astro";
|
||||
|
||||
const aboutPost = await getEntry('spec', 'about')
|
||||
|
||||
const { Content } = await aboutPost.render()
|
||||
|
||||
---
|
||||
<MainGridLayout title={i18n(I18nKey.about)} description={i18n(I18nKey.about)}>
|
||||
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative min-h-32">
|
||||
<div class="card-base z-10 px-9 py-6 relative w-full ">
|
||||
<Markdown class="mt-2">
|
||||
<Content />
|
||||
</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
</MainGridLayout>
|
25
src/pages/archive/category/[category].astro
Normal file
25
src/pages/archive/category/[category].astro
Normal file
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
import {getCategoryList, getSortedPosts} from "@utils/content-utils";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import {i18n} from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const categories = await getCategoryList();
|
||||
return categories.map(category => {
|
||||
return {
|
||||
params: {
|
||||
category: category.name
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const { category } = Astro.params;
|
||||
|
||||
---
|
||||
|
||||
<MainGridLayout title={i18n(I18nKey.archive)} description={i18n(I18nKey.archive)}>
|
||||
<ArchivePanel categories={[category]}></ArchivePanel>
|
||||
</MainGridLayout>
|
11
src/pages/archive/category/uncategorized.astro
Normal file
11
src/pages/archive/category/uncategorized.astro
Normal file
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import {i18n} from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import {UNCATEGORIZED} from "@constants/constants";
|
||||
---
|
||||
|
||||
<MainGridLayout title={i18n(I18nKey.archive)}>
|
||||
<ArchivePanel categories={[UNCATEGORIZED]}></ArchivePanel>
|
||||
</MainGridLayout>
|
12
src/pages/archive/index.astro
Normal file
12
src/pages/archive/index.astro
Normal file
|
@ -0,0 +1,12 @@
|
|||
---
|
||||
import { getCollection, getEntry } from "astro:content";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import {i18n} from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
---
|
||||
|
||||
<MainGridLayout title={i18n(I18nKey.archive)}>
|
||||
<ArchivePanel></ArchivePanel>
|
||||
</MainGridLayout>
|
||||
|
34
src/pages/archive/tag/[tag].astro
Normal file
34
src/pages/archive/tag/[tag].astro
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
import {getSortedPosts} from "@utils/content-utils";
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import ArchivePanel from "@components/ArchivePanel.astro";
|
||||
import {i18n} from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
|
||||
|
||||
export async function getStaticPaths() {
|
||||
let posts = await getSortedPosts()
|
||||
|
||||
const allTags = posts.reduce((acc, post) => {
|
||||
post.data.tags.forEach(tag => acc.add(tag));
|
||||
return acc;
|
||||
}, new Set());
|
||||
|
||||
const allTagsArray = Array.from(allTags);
|
||||
|
||||
return allTagsArray.map(tag => {
|
||||
return {
|
||||
params: {
|
||||
tag: tag
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const { tag } = Astro.params;
|
||||
|
||||
---
|
||||
|
||||
<MainGridLayout title={i18n(I18nKey.archive)} description={i18n(I18nKey.archive)}>
|
||||
<ArchivePanel tags={[tag]}></ArchivePanel>
|
||||
</MainGridLayout>
|
139
src/pages/posts/[...slug].astro
Normal file
139
src/pages/posts/[...slug].astro
Normal file
|
@ -0,0 +1,139 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content';
|
||||
import MainGridLayout from "@layouts/MainGridLayout.astro";
|
||||
import ImageWrapper from "../../components/misc/ImageWrapper.astro";
|
||||
import {Icon} from "astro-icon/components";
|
||||
import PostMetadata from "../../components/PostMeta.astro";
|
||||
import {i18n} from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import {getDir, getPostUrlBySlug} from "@utils/url-utils";
|
||||
import License from "@components/misc/License.astro";
|
||||
import {licenseConfig} from "src/config";
|
||||
import Markdown from "@components/misc/Markdown.astro";
|
||||
import path from "path";
|
||||
import {profileConfig} from "../../config";
|
||||
import {formatDateToYYYYMMDD} from "../../utils/date-utils";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const blogEntries = await getCollection('posts', ({ data }) => {
|
||||
return import.meta.env.PROD ? data.draft !== true : true;
|
||||
});
|
||||
return blogEntries.map(entry => ({
|
||||
params: { slug: entry.slug }, props: { entry },
|
||||
}));
|
||||
}
|
||||
|
||||
const { entry } = Astro.props;
|
||||
const { Content } = await entry.render();
|
||||
|
||||
const { remarkPluginFrontmatter } = await entry.render();
|
||||
|
||||
const jsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BlogPosting",
|
||||
"headline": entry.data.title,
|
||||
"description": entry.data.description || entry.data.title,
|
||||
"keywords": entry.data.tags,
|
||||
"author": {
|
||||
"@type": "Person",
|
||||
"name": profileConfig.name,
|
||||
"url": Astro.site
|
||||
},
|
||||
"datePublished": formatDateToYYYYMMDD(entry.data.published),
|
||||
// TODO include cover image here
|
||||
}
|
||||
|
||||
---
|
||||
<MainGridLayout banner={entry.data.image} title={entry.data.title} description={entry.data.description}>
|
||||
<script slot="head" type="application/ld+json" set:html={JSON.stringify(jsonLd)}></script>
|
||||
<div class="flex w-full rounded-[var(--radius-large)] overflow-hidden relative mb-4">
|
||||
<div id="post-container" class:list={["card-base z-10 px-6 md:px-9 pt-6 pb-4 relative w-full ",
|
||||
{}
|
||||
]}>
|
||||
<!-- word count and reading time -->
|
||||
<div class="flex flex-row text-black/30 dark:text-white/30 gap-5 mb-3 transition onload-animation">
|
||||
<div class="flex flex-row items-center">
|
||||
<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>
|
||||
</div>
|
||||
<div class="text-sm">{remarkPluginFrontmatter.words} {" " + i18n(I18nKey.wordsCount)}</div>
|
||||
</div>
|
||||
<div class="flex flex-row items-center">
|
||||
<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>
|
||||
</div>
|
||||
<div class="text-sm">{remarkPluginFrontmatter.minutes} {" " + i18n(I18nKey.minutesCount)}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- title -->
|
||||
<div class="relative onload-animation">
|
||||
<div
|
||||
data-pagefind-body data-pagefind-weight="10" data-pagefind-meta="title"
|
||||
class="transition w-full block font-bold mb-3
|
||||
text-3xl md:text-[2.5rem]/[2.75rem]
|
||||
text-black/90 dark:text-white/90
|
||||
md:before:w-1 before:h-5 before:rounded-md before:bg-[var(--primary)]
|
||||
before:absolute before:top-[0.75rem] before:left-[-1.125rem]
|
||||
">
|
||||
{entry.data.title}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- metadata -->
|
||||
<div class="onload-animation">
|
||||
<PostMetadata
|
||||
class="mb-5"
|
||||
published={entry.data.published}
|
||||
tags={entry.data.tags}
|
||||
category={entry.data.category}
|
||||
></PostMetadata>
|
||||
{!entry.data.image && <div class="border-[var(--line-divider)] border-dashed border-b-[1px] mb-5"></div>}
|
||||
</div>
|
||||
|
||||
<!-- always show cover as long as it has one -->
|
||||
|
||||
{entry.data.image &&
|
||||
<ImageWrapper src={entry.data.image} basePath={path.join("content/posts/", getDir(entry.id))} class="mb-8 rounded-xl banner-container onload-animation"/>
|
||||
}
|
||||
|
||||
|
||||
<Markdown class="mb-6 markdown-content onload-animation">
|
||||
<Content />
|
||||
</Markdown>
|
||||
|
||||
{licenseConfig.enable && <License title={entry.data.title} slug={entry.slug} pubDate={entry.data.published} class="mb-6 rounded-xl license-container onload-animation"></License>}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col md:flex-row justify-between mb-4 gap-4 overflow-hidden w-full">
|
||||
<a href={getPostUrlBySlug(entry.data.nextSlug)} class="w-full font-bold overflow-hidden active:scale-95">
|
||||
{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" size={32} class="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>
|
||||
</div>}
|
||||
</a>
|
||||
|
||||
<a href={getPostUrlBySlug(entry.data.prevSlug)} class="w-full font-bold overflow-hidden active:scale-95">
|
||||
{entry.data.prevSlug && <div class="btn-card rounded-2xl w-full h-[3.75rem] max-w-full px-4 flex items-center justify-end gap-4">
|
||||
<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" size={32} class="text-[var(--primary)]" />
|
||||
</div>}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</MainGridLayout>
|
||||
|
||||
<style is:global>
|
||||
#post-container :nth-child(1) { animation-delay: calc(var(--content-delay) + 0ms) }
|
||||
#post-container :nth-child(2) { animation-delay: calc(var(--content-delay) + 50ms) }
|
||||
#post-container :nth-child(3) { animation-delay: calc(var(--content-delay) + 100ms) }
|
||||
#post-container :nth-child(4) { animation-delay: calc(var(--content-delay) + 175ms) }
|
||||
#post-container :nth-child(5) { animation-delay: calc(var(--content-delay) + 250ms) }
|
||||
#post-container :nth-child(6) { animation-delay: calc(var(--content-delay) + 325ms) }
|
||||
</style>
|
16
src/pages/robots.txt.ts
Normal file
16
src/pages/robots.txt.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import type { APIRoute } from 'astro';
|
||||
|
||||
const robotsTxt = `
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Sitemap: ${new URL('sitemap-index.xml', import.meta.env.SITE).href}
|
||||
`.trim();
|
||||
|
||||
export const GET: APIRoute = () => {
|
||||
return new Response(robotsTxt, {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
},
|
||||
});
|
||||
};
|
25
src/pages/rss.xml.ts
Normal file
25
src/pages/rss.xml.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import rss from '@astrojs/rss';
|
||||
import {siteConfig} from '@/config';
|
||||
import { getCollection } from 'astro:content';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
import MarkdownIt from 'markdown-it';
|
||||
const parser = new MarkdownIt();
|
||||
|
||||
export async function GET(context: any) {
|
||||
const blog = await getCollection('posts');
|
||||
return rss({
|
||||
title: siteConfig.title,
|
||||
description: siteConfig.subtitle || 'No description',
|
||||
site: context.site,
|
||||
items: blog.map((post) => ({
|
||||
title: post.data.title,
|
||||
pubDate: post.data.published,
|
||||
description: post.data.description,
|
||||
link: `/posts/${post.slug}/`,
|
||||
content: sanitizeHtml(parser.render(post.body), {
|
||||
allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img'])
|
||||
}),
|
||||
})),
|
||||
customData: `<language>${siteConfig.lang}</language>`,
|
||||
});
|
||||
}
|
Reference in a new issue