feat!: add file to src
This commit is contained in:
parent
aa011faa54
commit
90eaaa8ae4
24 changed files with 471 additions and 0 deletions
24
src/components/ScrollToTop.vue
Normal file
24
src/components/ScrollToTop.vue
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { useWindowScroll } from '@vueuse/core'
|
||||||
|
|
||||||
|
const { y: scroll } = useWindowScroll()
|
||||||
|
|
||||||
|
function toTop() {
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'smooth',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<button
|
||||||
|
aria-label="Scroll to top"
|
||||||
|
fixed right-5 sm:right-30 bottom-30 w-12 h-12 text-lg hover:op100 rounded-full flex="~ items-center justify-center"
|
||||||
|
bg-hex-8883 transition duration-300 z-100 print:hidden
|
||||||
|
:class="scroll > 300 ? 'op75' : 'op0 pointer-events-none'"
|
||||||
|
@click="toTop()"
|
||||||
|
>
|
||||||
|
<i i-ri-arrow-up-line />
|
||||||
|
</button>
|
||||||
|
</template>
|
7
src/content/blog/note/post-1.md
Normal file
7
src/content/blog/note/post-1.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
title: Note Name
|
||||||
|
duration: 5min
|
||||||
|
date: 2022-12-01
|
||||||
|
---
|
||||||
|
|
||||||
|
Writing...
|
7
src/content/blog/note/post-2.md
Normal file
7
src/content/blog/note/post-2.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
title: Note Name
|
||||||
|
duration: 5min
|
||||||
|
date: 2018-01-01
|
||||||
|
---
|
||||||
|
|
||||||
|
Writing...
|
7
src/content/blog/note/post-3.md
Normal file
7
src/content/blog/note/post-3.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
title: Note Name
|
||||||
|
duration: 5min
|
||||||
|
date: 2022-02-01
|
||||||
|
---
|
||||||
|
|
||||||
|
Writing...
|
7
src/content/blog/note/post-4.md
Normal file
7
src/content/blog/note/post-4.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
title: Note Name
|
||||||
|
duration: 5min
|
||||||
|
date: 2024-03-01
|
||||||
|
---
|
||||||
|
|
||||||
|
Writing...
|
7
src/content/blog/post-1.md
Normal file
7
src/content/blog/post-1.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
title: Blog Name
|
||||||
|
description: Your blog description, which is long text, can be an introduction to the post or a paragraph of the post.
|
||||||
|
date: 2024-05-01
|
||||||
|
---
|
||||||
|
|
||||||
|
Writing...
|
8
src/content/blog/post-2.md
Normal file
8
src/content/blog/post-2.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
title: Blog Name
|
||||||
|
description: If you set the lang field to include zh, it will display an identifier.
|
||||||
|
date: 2024-04-01
|
||||||
|
lang: zh
|
||||||
|
---
|
||||||
|
|
||||||
|
Writing...
|
8
src/content/blog/post-3.md
Normal file
8
src/content/blog/post-3.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
title: Blog Name
|
||||||
|
description: A tag field is provided, which can be used to display custom information.
|
||||||
|
date: 2024-03-01
|
||||||
|
tag: Tag Text
|
||||||
|
---
|
||||||
|
|
||||||
|
Writing...
|
6
src/content/blog/post-4.md
Normal file
6
src/content/blog/post-4.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: Blog Name
|
||||||
|
description: If you need to link to an external address, you can set the redirect field.
|
||||||
|
date: 2024-02-01
|
||||||
|
redirect: '/'
|
||||||
|
---
|
6
src/content/blog/post-5.md
Normal file
6
src/content/blog/post-5.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: Blog Name
|
||||||
|
description: If you want to tell the visitor that there is a video, you can set the video field to true.
|
||||||
|
date: 2024-02-01
|
||||||
|
video: true
|
||||||
|
---
|
5
src/content/blog/post-6.md
Normal file
5
src/content/blog/post-6.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Blog Name
|
||||||
|
description: Vitesse theme groups posts by year and displays them sorted by date.
|
||||||
|
date: 2023-08-01
|
||||||
|
---
|
6
src/content/blog/post-7.md
Normal file
6
src/content/blog/post-7.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: Blog Name
|
||||||
|
description: You can set a duration to tell the viewer how long it will take to watch it。
|
||||||
|
duration: 20min
|
||||||
|
date: 2023-05-01
|
||||||
|
---
|
5
src/content/blog/post-8.md
Normal file
5
src/content/blog/post-8.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Blog Name
|
||||||
|
description: The date and title fields are required.
|
||||||
|
date: 2022-11-01
|
||||||
|
---
|
6
src/content/pages/page-1.md
Normal file
6
src/content/pages/page-1.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: Sponsor
|
||||||
|
description: How to sponsor for me.
|
||||||
|
---
|
||||||
|
|
||||||
|
Writing...
|
6
src/content/pages/page-2.md
Normal file
6
src/content/pages/page-2.md
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: Sponsor
|
||||||
|
description: How to sponsor for me.
|
||||||
|
---
|
||||||
|
|
||||||
|
Writing...
|
49
src/pages/blog/[...path].astro
Normal file
49
src/pages/blog/[...path].astro
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
---
|
||||||
|
import BaseLayout from '@/layouts/BaseLayout.astro'
|
||||||
|
import ListPosts from '@/components/ListPosts.vue'
|
||||||
|
import { getPosts } from '@/utils/posts'
|
||||||
|
import siteConfig from '@/site-config'
|
||||||
|
import { uniqBy } from 'lodash-es'
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const blogEntries = await getPosts()
|
||||||
|
return uniqBy(
|
||||||
|
blogEntries.map((entry) => {
|
||||||
|
return {
|
||||||
|
params: {
|
||||||
|
path: entry.slug.includes('/')
|
||||||
|
? entry.slug.split('/').shift()
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
'params.path',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { path } = Astro.params
|
||||||
|
|
||||||
|
const posts = await getPosts(path)
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout
|
||||||
|
title="Blog"
|
||||||
|
description="List of all the blog posts."
|
||||||
|
pageNav={true}
|
||||||
|
pageOperate={true}
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-2 sm:flex-row sm:gap-4 flex-wrap mb-8">
|
||||||
|
{
|
||||||
|
siteConfig.page.navLinks.map((nav) => (
|
||||||
|
<a
|
||||||
|
aria-label={nav.text}
|
||||||
|
class={`nav-link text-3xl font-bold ${Astro.url.pathname === nav.href ? 'opacity-80' : 'opacity-30 hover:opacity-50'}`}
|
||||||
|
href={nav.href}
|
||||||
|
>
|
||||||
|
{nav.text}
|
||||||
|
</a>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<ListPosts list={posts} />
|
||||||
|
</BaseLayout>
|
61
src/pages/posts/[...slug].astro
Normal file
61
src/pages/posts/[...slug].astro
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
import BaseLayout from '@/layouts/BaseLayout.astro'
|
||||||
|
import { type CollectionPosts } from '@/types'
|
||||||
|
import { getPosts } from '@/utils/posts'
|
||||||
|
|
||||||
|
export async function getStaticPaths() {
|
||||||
|
const posts = await getPosts()
|
||||||
|
return posts.map((post) => {
|
||||||
|
return {
|
||||||
|
params: { slug: post.slug },
|
||||||
|
props: {
|
||||||
|
post,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = { post: CollectionPosts }
|
||||||
|
|
||||||
|
const { post } = Astro.props
|
||||||
|
const { title, image, description, date, duration, tag } = post.data
|
||||||
|
|
||||||
|
const { Content } = await post.render()
|
||||||
|
|
||||||
|
function getDate(date: string) {
|
||||||
|
return new Date(date).toISOString()
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout
|
||||||
|
title={title}
|
||||||
|
description={description}
|
||||||
|
pageType="article"
|
||||||
|
pageOperate={true}
|
||||||
|
>
|
||||||
|
<article class="mb-16 max-w-none prose">
|
||||||
|
<h1 class="text-title">
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
|
<p op-50>
|
||||||
|
{date && <time datetime={getDate(date)}>{date.split(',')}</time>}
|
||||||
|
{duration && <span>· {duration}</span>}
|
||||||
|
{tag && <span>· {tag}</span>}
|
||||||
|
</p>
|
||||||
|
{
|
||||||
|
image && (
|
||||||
|
<p>
|
||||||
|
<img
|
||||||
|
width="100%"
|
||||||
|
height="auto"
|
||||||
|
src={image.src}
|
||||||
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
|
alt={image.alt || ''}
|
||||||
|
/>
|
||||||
|
</p>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<Content />
|
||||||
|
</article>
|
||||||
|
</BaseLayout>
|
88
src/pages/projects/data.ts
Normal file
88
src/pages/projects/data.ts
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import type { ProjectData } from '@/types'
|
||||||
|
|
||||||
|
export const projectData: ProjectData = [
|
||||||
|
{
|
||||||
|
title: 'Projects Group',
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Projects Group',
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Project Name',
|
||||||
|
projects: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Projects Group',
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Projects Group',
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
text: 'Project Name',
|
||||||
|
description: 'Your project description information is a long piece of text.',
|
||||||
|
icon: 'i-carbon-campsite',
|
||||||
|
href: '/',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
31
src/pages/projects/index.astro
Normal file
31
src/pages/projects/index.astro
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
import { projectData } from './data'
|
||||||
|
import BaseLayout from '@/layouts/BaseLayout.astro'
|
||||||
|
import ListProjects from '@/components/ListProjects.vue'
|
||||||
|
---
|
||||||
|
|
||||||
|
<BaseLayout
|
||||||
|
title="Projects"
|
||||||
|
description="List of projects that I am proud of"
|
||||||
|
pageOperate={true}
|
||||||
|
>
|
||||||
|
<h1 class="mb-10 text-title">Projects</h1>
|
||||||
|
<div mb-16 sm:mb-24>
|
||||||
|
{
|
||||||
|
projectData.length > 0 && (
|
||||||
|
<div>
|
||||||
|
{projectData.map((i) => (
|
||||||
|
<div mb-10>
|
||||||
|
<h2 class="select-none relative h20 pointer-events-none">
|
||||||
|
<span class="text-3.2em color-transparent font-bold text-stroke-1.5 text-stroke-hex-aaa op35 dark:op20 absolute top-0">
|
||||||
|
{i.title}
|
||||||
|
</span>
|
||||||
|
</h2>
|
||||||
|
<ListProjects list={i.projects} />
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</BaseLayout>
|
15
src/pages/robots.txt.ts
Normal file
15
src/pages/robots.txt.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
interface Context {
|
||||||
|
site: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GET(context: Context) {
|
||||||
|
const robots = `
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
Sitemap: ${new URL('sitemap-index.xml', context.site).href}`.trim()
|
||||||
|
|
||||||
|
return new Response(robots, {
|
||||||
|
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
|
||||||
|
})
|
||||||
|
}
|
26
src/pages/rss.xml.ts
Normal file
26
src/pages/rss.xml.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import rss from '@astrojs/rss'
|
||||||
|
import siteConfig from '@/site-config'
|
||||||
|
import { getPosts } from '@/utils/posts'
|
||||||
|
|
||||||
|
interface Context {
|
||||||
|
site: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function GET(context: Context) {
|
||||||
|
const posts = await getPosts()
|
||||||
|
|
||||||
|
return rss({
|
||||||
|
title: siteConfig.title,
|
||||||
|
description: siteConfig.description,
|
||||||
|
site: context.site,
|
||||||
|
items: posts!.map((item) => {
|
||||||
|
return {
|
||||||
|
...item.data,
|
||||||
|
link: `${context.site}/posts/${item.slug}/`,
|
||||||
|
pubDate: new Date(item.data.date),
|
||||||
|
content: item.body,
|
||||||
|
author: `${siteConfig.author} <${siteConfig.email}>`,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
24
src/styles/dot.css
Normal file
24
src/styles/dot.css
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
html.dark {
|
||||||
|
--dot-bg-color: #0d1117;
|
||||||
|
--dot-color: #2f353c;
|
||||||
|
--dot-mask-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
html:not(.dark) {
|
||||||
|
--dot-bg-color: #ffffff;
|
||||||
|
--dot-color: #a5aeb850;
|
||||||
|
--dot-mask-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-dot::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background:
|
||||||
|
linear-gradient(90deg, var(--dot-bg-color) 22px, transparent 1%) 50%,
|
||||||
|
linear-gradient(var(--dot-bg-color) 22px, transparent 1%) 50%,
|
||||||
|
var(--dot-color);
|
||||||
|
background-position: center center;
|
||||||
|
background-size: 24px 24px;
|
||||||
|
mask-image: linear-gradient(0deg, transparent 5%, var(--dot-mask-color));
|
||||||
|
}
|
59
src/styles/global.css
Normal file
59
src/styles/global.css
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
html.dark .astro-code,
|
||||||
|
html.dark .astro-code span {
|
||||||
|
color: var(--shiki-dark) !important;
|
||||||
|
background-color: #161b22 !important;
|
||||||
|
font-style: var(--shiki-dark-font-style) !important;
|
||||||
|
font-weight: var(--shiki-dark-font-weight) !important;
|
||||||
|
text-decoration: var(--shiki-dark-text-decoration) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html.dark {
|
||||||
|
color-scheme: dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
html:not(.dark) {
|
||||||
|
color-scheme: light;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nprogress .bar {
|
||||||
|
background: #888;
|
||||||
|
opacity: 0.75;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 999;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
--at-apply: rd-1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose-link i {
|
||||||
|
--at-apply: text-sm mr-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose a {
|
||||||
|
--at-apply: prose-link;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose h1 {
|
||||||
|
--at-apply: text-9 mb-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose h2 {
|
||||||
|
--at-apply: mt-16 mb-4 text-6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose h3 {
|
||||||
|
--at-apply: mt-10 mb-4 text-5;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
--at-apply: sm: min-h-38 min-h-28;
|
||||||
|
}
|
3
src/utils/link.ts
Normal file
3
src/utils/link.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export function getLinkTarget(link: string) {
|
||||||
|
return link.includes('http') ? '_blank' : '_self'
|
||||||
|
}
|
Loading…
Reference in a new issue