feat: init repo
This commit is contained in:
parent
9d8ba9ffcb
commit
e71b646de9
47 changed files with 8381 additions and 130 deletions
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
github: kaivanwong
|
31
.github/workflows/ci.yml
vendored
Normal file
31
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
|
||||
- name: Set node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: lts/*
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Lint
|
||||
run: pnpm lint
|
24
.github/workflows/release.yml
vendored
Normal file
24
.github/workflows/release.yml
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- run: npx changelogiter
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
134
.gitignore
vendored
134
.gitignore
vendored
|
@ -1,130 +1,6 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
node_modules
|
||||
.astro
|
||||
.DS_Store
|
||||
.eslintcache
|
||||
*.log
|
||||
|
|
11
.vscode/extensions.json
vendored
Normal file
11
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"astro-build.astro-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"usernamehw.errorlens",
|
||||
"vue.vscode-typescript-vue-plugin",
|
||||
"vue.volar",
|
||||
"antfu.unocss",
|
||||
"antfu.iconify"
|
||||
]
|
||||
}
|
70
.vscode/settings.json
vendored
Normal file
70
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
// Enable the ESlint flat config support
|
||||
"eslint.experimental.useFlatConfig": true,
|
||||
// Disable the default formatter, use eslint instead
|
||||
"prettier.enable": false,
|
||||
"editor.formatOnSave": false,
|
||||
// Auto fix
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
// Silent the stylistic rules in you IDE, but still auto fix them
|
||||
"eslint.rules.customizations": [
|
||||
{
|
||||
"rule": "style/*",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "format/*",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-indent",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spacing",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-spaces",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-order",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-dangle",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*-newline",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*quotes",
|
||||
"severity": "off"
|
||||
},
|
||||
{
|
||||
"rule": "*semi",
|
||||
"severity": "off"
|
||||
}
|
||||
],
|
||||
// Enable eslint for all supported languages
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"yaml",
|
||||
"toml",
|
||||
"astro"
|
||||
]
|
||||
}
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2024 Kaivan Wong
|
||||
Copyright (c) 2023 Kaivan Wong
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
36
README.md
36
README.md
|
@ -1,3 +1,39 @@
|
|||
# Vitesse theme for Astro
|
||||
|
||||
Vitesse theme for Astro blog, supports Vue and UnoCSS.
|
||||
|
||||
[![Netlify Status](https://api.netlify.com/api/v1/badges/d5bae292-6116-4c52-af4b-05eadedccc60/deploy-status)](https://app.netlify.com/sites/kaivanwong/deploys)
|
||||
|
||||
## Preview
|
||||
|
||||
![Preview Image](./preview.png)
|
||||
|
||||
## Quick Start
|
||||
|
||||
[![Deploy to Netlify Button](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/kaivanwong/astro-theme-vitesse)
|
||||
|
||||
If you click this button, it will create a new repo for you that looks exactly like this one, and sets that repo up immediately for deployment on Netlify.
|
||||
|
||||
## Usage
|
||||
|
||||
Just run and visit http://localhost:1977.
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
To build the App, you can run:
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
```
|
||||
|
||||
You will then see the `dist` folder generated for publishing, which you can preview locally with the following command.
|
||||
|
||||
```bash
|
||||
pnpm preview
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT License](./LICENSE) © 2024-PRESENT [Kaivan Wong](https://github.com/kaivanwong)
|
||||
|
|
29
astro.config.ts
Normal file
29
astro.config.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { defineConfig } from 'astro/config'
|
||||
import mdx from '@astrojs/mdx'
|
||||
import sitemap from '@astrojs/sitemap'
|
||||
import UnoCSS from 'unocss/astro'
|
||||
import vue from '@astrojs/vue'
|
||||
|
||||
export default defineConfig({
|
||||
site: 'https://astro-theme-vitesse.netlify.app/',
|
||||
server: {
|
||||
port: 1977,
|
||||
},
|
||||
integrations: [
|
||||
mdx(),
|
||||
sitemap(),
|
||||
UnoCSS({
|
||||
injectReset: true,
|
||||
}),
|
||||
vue(),
|
||||
],
|
||||
markdown: {
|
||||
shikiConfig: {
|
||||
themes: {
|
||||
light: 'vitesse-light',
|
||||
dark: 'vitesse-dark',
|
||||
},
|
||||
wrap: true,
|
||||
},
|
||||
},
|
||||
})
|
11
eslint.config.js
Normal file
11
eslint.config.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import antfu from '@antfu/eslint-config'
|
||||
|
||||
export default antfu({
|
||||
vue: true,
|
||||
typescript: true,
|
||||
astro: true,
|
||||
formatters: {
|
||||
astro: true,
|
||||
css: true,
|
||||
},
|
||||
})
|
48
package.json
Normal file
48
package.json
Normal file
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"name": "astro-theme-vitesse",
|
||||
"type": "module",
|
||||
"version": "0.0.0",
|
||||
"packageManager": "pnpm@8.11.0",
|
||||
"engines": {
|
||||
"node": ">=18.0"
|
||||
},
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"prepare": "simple-git-hooks",
|
||||
"dev": "astro dev --host",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"release": "bumpp"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/mdx": "^2.2.0",
|
||||
"@astrojs/rss": "^4.0.5",
|
||||
"@astrojs/sitemap": "^3.1.1",
|
||||
"@astrojs/vue": "^4.0.8",
|
||||
"@unocss/reset": "^0.58.5",
|
||||
"astro": "^4.5.2",
|
||||
"marked": "^11.2.0",
|
||||
"unocss": "^0.58.5",
|
||||
"vue": "^3.4.21"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "^2.8.1",
|
||||
"@iconify/json": "^2.2.191",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"bumpp": "^9.4.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-astro": "^0.31.4",
|
||||
"eslint-plugin-format": "^0.1.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"prettier-plugin-astro": "^0.13.0",
|
||||
"simple-git-hooks": "^2.10.0"
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"pre-commit": "pnpm lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": "pnpm lint:fix"
|
||||
}
|
||||
}
|
6842
pnpm-lock.yaml
Normal file
6842
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
BIN
preview.png
Normal file
BIN
preview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 179 KiB |
BIN
public/about.jpg
Normal file
BIN
public/about.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 38 KiB |
177
public/favicon.svg
Normal file
177
public/favicon.svg
Normal file
|
@ -0,0 +1,177 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="300px" height="300px"><svg version="1.1" id="SvgjsSvg1001" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="300px" height="300px" viewBox="0 0 300 300" enable-background="new 0 0 300 300" xml:space="preserve"> <image id="SvgjsImage1000" width="300" height="300" x="0" y="0" href="data:image/png;base64,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"></image>
|
||||
</svg><style>@media (prefers-color-scheme: light) { :root { filter: contrast(1) brightness(1); } }
|
||||
@media (prefers-color-scheme: dark) { :root { filter: invert(100%); } }
|
||||
</style></svg>
|
After Width: | Height: | Size: 14 KiB |
BIN
public/hero.jpg
Normal file
BIN
public/hero.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 45 KiB |
89
src/components/BaseHead.astro
Normal file
89
src/components/BaseHead.astro
Normal file
|
@ -0,0 +1,89 @@
|
|||
---
|
||||
import siteConfig from '../site-config'
|
||||
import '../styles/markdown.css'
|
||||
|
||||
export type Props = {
|
||||
title?: string
|
||||
description?: string
|
||||
image?: { src: string; alt?: string }
|
||||
pageType?: 'website' | 'article'
|
||||
}
|
||||
|
||||
const {
|
||||
description = '',
|
||||
image = siteConfig.image,
|
||||
pageType = 'website',
|
||||
} = Astro.props
|
||||
const title = [Astro.props.title, siteConfig.title].filter(Boolean).join(' | ')
|
||||
const resolvedImage = image?.src
|
||||
? {
|
||||
src: new URL(image.src, Astro.site).toString(),
|
||||
alt: image.alt,
|
||||
}
|
||||
: undefined
|
||||
const canonicalURL = new URL(Astro.request.url, Astro.site)
|
||||
|
||||
/**
|
||||
* Enforce some standard canonical URL formatting across the site.
|
||||
*/
|
||||
function formatCanonicalURL(url: string | URL) {
|
||||
const path = url.toString()
|
||||
const hasQueryParams = path.includes('?')
|
||||
// If there are query params, make sure the URL has no trailing slash
|
||||
if (hasQueryParams) path.replace(/\/?$/, '')
|
||||
|
||||
// otherwise, canonical URL always has a trailing slash
|
||||
return path.replace(/\/?$/, hasQueryParams ? '' : '/')
|
||||
}
|
||||
---
|
||||
|
||||
<!-- High Priority Global Metadata -->
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<title>{title}</title>
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&family=Newsreader:ital,opsz,wght@0,6..72,400..700;1,6..72,400..700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<!-- Low Priority Global Metadata -->
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
<link rel="alternate" type="application/rss+xml" href="/rss.xml" title="RSS" />
|
||||
|
||||
<!-- Page Metadata -->
|
||||
<link rel="canonical" href={formatCanonicalURL(canonicalURL)} />
|
||||
<meta name="description" content={description} />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:type" content={pageType} />
|
||||
<meta property="og:url" content={formatCanonicalURL(canonicalURL)} />
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
{resolvedImage?.src && <meta property="og:image" content={resolvedImage.src} />}
|
||||
{
|
||||
resolvedImage?.alt && (
|
||||
<meta property="og:image:alt" content={resolvedImage.alt} />
|
||||
)
|
||||
}
|
||||
|
||||
<!-- X/Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={formatCanonicalURL(canonicalURL)} />
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
{
|
||||
resolvedImage?.src && (
|
||||
<meta property="twitter:image" content={resolvedImage.src} />
|
||||
)
|
||||
}
|
||||
{
|
||||
resolvedImage?.alt && (
|
||||
<meta name="twitter:image:alt" content={resolvedImage?.alt} />
|
||||
)
|
||||
}
|
17
src/components/Footer.vue
Normal file
17
src/components/Footer.vue
Normal file
|
@ -0,0 +1,17 @@
|
|||
<script lang="ts" setup>
|
||||
import siteConfig from '../site-config'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<footer class="w-full px-6 pt-4 pb-12 max-w-3xl mx-auto opacity-60">
|
||||
<div class="mb-6 flex flex-wrap justify-center gap-x-4 gap-y-2">
|
||||
<a v-for="link in siteConfig.footerNavLinks" :key="link.text" class="nav-link" :href="link.href">
|
||||
{{ link.text }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="flex flex-wrap justify-center text-dark dark:text-white">
|
||||
<span class="opacity-70">2023-PRESENT ©</span>
|
||||
<a class="!nav-link opacity-100 ml-1" href="/">{{ siteConfig.author }}</a>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
48
src/components/Header.vue
Normal file
48
src/components/Header.vue
Normal file
|
@ -0,0 +1,48 @@
|
|||
<script lang="ts" setup>
|
||||
import { ref, watchEffect } from 'vue'
|
||||
import { onClickOutside, useWindowSize } from '@vueuse/core'
|
||||
import siteConfig from '../site-config'
|
||||
import ThemeToggle from './ThemeToggle.vue'
|
||||
|
||||
const navLinks = siteConfig.headerNavLinks || []
|
||||
|
||||
const menuRef = ref(null)
|
||||
|
||||
const menu = ref(false)
|
||||
|
||||
function toggleMenu() {
|
||||
menu.value = !menu.value
|
||||
}
|
||||
|
||||
const { width } = useWindowSize()
|
||||
|
||||
onClickOutside(menuRef, () => {
|
||||
if (width.value < 640)
|
||||
menu.value = false
|
||||
})
|
||||
|
||||
watchEffect(() => {
|
||||
if (width.value > 640)
|
||||
menu.value = true
|
||||
else
|
||||
menu.value = false
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header text-lg max-w-3xl mx-auto h-18 px-6 flex justify-between items-center relative>
|
||||
<nav
|
||||
v-show="menu" ref="menuRef" flex flex-wrap gap-4 sm:gap-6 sm:position-initial absolute z-199 top-15 sm:flex-row
|
||||
flex-col sm:p0 p-4 bg-main border-1 border-main sm:border-none
|
||||
>
|
||||
<a v-for="link in navLinks" :key="link.text" nav-link :href="link.href">
|
||||
{{ link.text }}
|
||||
</a>
|
||||
</nav>
|
||||
<menu sm:hidden inline-block i-ri-menu-2-fill @click="toggleMenu" />
|
||||
<div flex gap-4 sm:gap-6>
|
||||
<a nav-link href="rss.xml" i-ri-rss-line />
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
77
src/components/ListPosts.vue
Normal file
77
src/components/ListPosts.vue
Normal file
|
@ -0,0 +1,77 @@
|
|||
<script lang="ts" setup>
|
||||
interface Posts {
|
||||
id: string
|
||||
slug: string
|
||||
body: string
|
||||
data: Record<string, any>
|
||||
collection: string
|
||||
render: any
|
||||
}
|
||||
|
||||
withDefaults(defineProps<{
|
||||
list: Posts[]
|
||||
}>(), {
|
||||
list: () => [],
|
||||
})
|
||||
|
||||
function getDate(date: string) {
|
||||
return new Date(date).toISOString()
|
||||
}
|
||||
|
||||
function getHref(posts: Posts) {
|
||||
if (posts.data.redirect)
|
||||
return posts.data.redirect
|
||||
return `/posts/${posts.slug}`
|
||||
}
|
||||
|
||||
function getTarget(posts: Posts) {
|
||||
if (posts.data.redirect)
|
||||
return '_blank'
|
||||
return '_self'
|
||||
}
|
||||
|
||||
function isSameYear(a: Date | string | number, b: Date | string | number) {
|
||||
return a && b && getYear(a) === getYear(b)
|
||||
}
|
||||
|
||||
function getYear(date: Date | string | number) {
|
||||
return new Date(date).getFullYear()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul>
|
||||
<template v-if="!list || list.length === 0">
|
||||
<div py2 opacity-50>
|
||||
nothing here yet.
|
||||
</div>
|
||||
</template>
|
||||
<li v-for="(posts, index) in list " :key="posts.data.title" mb-6>
|
||||
<div v-if="!isSameYear(posts.data.date, list[index - 1]?.data.date)" select-none relative h18 pointer-events-none>
|
||||
<span text-7em color-transparent font-bold text-stroke-2 text-stroke-hex-aaa op14 absolute top--0.2em>
|
||||
{{ getYear(posts.data.date) }}
|
||||
</span>
|
||||
</div>
|
||||
<a text-lg lh-tight nav-link flex="~ col gap-2" :target="getTarget(posts)" :href="getHref(posts)">
|
||||
<div flex="~ col md:row gap-2 md:items-center">
|
||||
<div flex="~ gap-2 items-center text-wrap">
|
||||
<span lh-normal>
|
||||
<i v-if="posts.data.draft" text-base vertical-mid i-ri-draft-line />
|
||||
{{ posts.data.title }}
|
||||
</span>
|
||||
</div>
|
||||
<div opacity-50 text-sm ws-nowrap flex="~ gap-2 items-center">
|
||||
<i v-if="posts.data.redirect" text-base i-ri-external-link-line />
|
||||
<i v-if="posts.data.recording || posts.data.video" text-base i-ri:film-line />
|
||||
<time :datetime="getDate(posts.data.date)">{{ posts.data.date.split(',')[0] }}</time>
|
||||
<span v-if="posts.data.duration">· {{ posts.data.duration }}</span>
|
||||
<span v-if="posts.data.tag">· {{ posts.data.tag }}</span>
|
||||
<span v-if="posts.data.lang.includes('zh')">· 中文</span>
|
||||
<span v-if="posts.data.link">· 中文</span>
|
||||
</div>
|
||||
</div>
|
||||
<div opacity-50 text-sm>{{ posts.data.description }}</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
31
src/components/ListProjects.vue
Normal file
31
src/components/ListProjects.vue
Normal file
|
@ -0,0 +1,31 @@
|
|||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
list: {
|
||||
text: string
|
||||
description: string
|
||||
icon?: string
|
||||
href: string
|
||||
}[]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ul grid="~ cols-1 sm:cols-2 gap-4">
|
||||
<template v-if="!list || list.length === 0">
|
||||
<div py2 opacity-50>
|
||||
nothing here yet.
|
||||
</div>
|
||||
</template>
|
||||
<li v-for="project in list" :key="project.text" container-link w-full flex items-center>
|
||||
<a flex items-center target="_blank" :href="project.href">
|
||||
<div ml-2 mr-4 pt-2>
|
||||
<i text-4xl inline-block :class="project.icon || 'i-carbon-unknown'" />
|
||||
</div>
|
||||
<div font-normal lh-tight>
|
||||
<div text-lg hover:text-main>{{ project.text }}</div>
|
||||
<div opacity-50 text-sm>{{ project.description }}</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
18
src/components/PageNav.vue
Normal file
18
src/components/PageNav.vue
Normal file
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts" setup>
|
||||
import siteConfig from '../site-config'
|
||||
|
||||
defineProps<{
|
||||
pathname: string
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div flex="~ col gap-2 sm:row sm:gap-4 wrap" mb-8>
|
||||
<a
|
||||
v-for="nav in siteConfig.pageNavLinks" :key="nav.text" nav-link text-3xl font-bold
|
||||
:class="pathname.includes(nav.href) ? 'opacity-80' : 'opacity-30 hover:opacity-50'" :href="nav.href"
|
||||
>
|
||||
{{ nav.text }}
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
55
src/components/PageOperate.vue
Normal file
55
src/components/PageOperate.vue
Normal file
|
@ -0,0 +1,55 @@
|
|||
<script lang="ts" setup>
|
||||
import { useWindowScroll } from '@vueuse/core'
|
||||
|
||||
withDefaults(defineProps<{
|
||||
showShare?: boolean
|
||||
showBack?: boolean
|
||||
url?: URL
|
||||
}>(), {
|
||||
showShare: false,
|
||||
showBack: true,
|
||||
})
|
||||
|
||||
const shareLinks = [
|
||||
{
|
||||
text: 'Twitter',
|
||||
icon: 'i-simple-icons-x',
|
||||
href: 'https://twitter.com/intent/tweet?url=',
|
||||
},
|
||||
{
|
||||
text: 'Mail',
|
||||
href: 'mailto:?subject=See%20this%20post&body=',
|
||||
},
|
||||
]
|
||||
|
||||
const { y: scroll } = useWindowScroll()
|
||||
|
||||
function toTop() {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div sm:flex="~ flex-row items-start justify-between" w-full font-mono opacity-50 text-main>
|
||||
<div>
|
||||
<div v-if="showShare" flex="~ gap-2 items-center flex-wrap" mb-2>
|
||||
<i i-ri-arrow-right-s-line />
|
||||
<span>share to</span>
|
||||
<a v-for="link in shareLinks" :key="link.text" prose-link lh-tight :href="link.href + url">
|
||||
<i v-if="link.icon" text-2.2 :class="link.icon" mr-0.8 />{{ link.text }}
|
||||
</a>
|
||||
</div>
|
||||
<div v-if="showBack" flex="~ gap-2 items-center">
|
||||
<i i-ri-arrow-right-s-line />
|
||||
<a prose-link href="javascript:history.back(-1)">cd ..</a>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="scroll > 300" cursor-pointer sm:m-0 mt-6 flex="~ gap-2 items-center" sm:gap-2>
|
||||
<i vertical-mid i-ri-arrow-up-line />
|
||||
<span prose-link @click="toTop()">Scroll to top</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
10
src/components/ThemeToggle.vue
Normal file
10
src/components/ThemeToggle.vue
Normal file
|
@ -0,0 +1,10 @@
|
|||
<script lang="ts" setup>
|
||||
import { useDark, useToggle } from '@vueuse/core'
|
||||
|
||||
const isDark = useDark()
|
||||
const toggleDark = useToggle(isDark)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<i nav-link dark:i-ri-moon-line i-ri-sun-line @click="toggleDark()" />
|
||||
</template>
|
32
src/content/blog/responsive-user-interfaces-vue.md
Normal file
32
src/content/blog/responsive-user-interfaces-vue.md
Normal file
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
title: "Responsive User Interfaces with Vue 3"
|
||||
description: Creating Dynamic and Responsive User Interfaces with Vue 3
|
||||
duration: "12min"
|
||||
date: "2023-08-11"
|
||||
---
|
||||
|
||||
In the realm of modern web development, creating dynamic and responsive user interfaces is paramount to delivering engaging and immersive web experiences. With the advent of Vue 3, developers now have a powerful toolkit at their disposal to craft dynamic, reactive, and high-performing user interfaces like never before. In this article, we'll explore the key features of Vue 3 and demonstrate how to leverage its capabilities to create dynamic and responsive user interfaces.
|
||||
|
||||
## Understanding Vue 3: A Paradigm Shift
|
||||
|
||||
Vue 3 represents a significant evolution of the Vue.js framework, introducing several groundbreaking features and performance improvements. One of the most notable enhancements is the Composition API, which provides a more flexible and scalable way to organize and reuse code logic within Vue components. With the Composition API, developers can encapsulate related functionality into reusable composition functions, leading to cleaner and more maintainable codebases.
|
||||
|
||||
## Reactivity at its Core
|
||||
|
||||
At the heart of Vue 3 lies its robust reactivity system, which enables seamless and efficient data binding between the underlying data model and the user interface. Vue 3's reactivity system is powered by the Composition API and the new reactive and ref APIs, which allow developers to create reactive data objects and references that automatically update the user interface whenever their values change. This reactive paradigm simplifies state management and enables developers to build highly interactive and responsive user interfaces with minimal effort.
|
||||
|
||||
## Leveraging Vue 3's Composition API
|
||||
|
||||
The Composition API is a game-changer for Vue.js development, offering a more flexible and intuitive way to organize and reuse code logic within components. Unlike the Options API, which relies on fixed lifecycle hooks and options objects, the Composition API allows developers to define component logic in a more granular and composable manner using standalone functions called composition functions.
|
||||
|
||||
By breaking down component logic into smaller, reusable composition functions, developers can better encapsulate and manage complex functionality within their components. This promotes code reuse, improves readability, and facilitates collaboration among team members. Additionally, the Composition API enables better separation of concerns and allows developers to extract common logic into reusable mixins, further enhancing code maintainability and scalability.
|
||||
|
||||
## Building Dynamic User Interfaces
|
||||
|
||||
With Vue 3's reactivity system and Composition API in hand, developers can easily build dynamic user interfaces that respond to user interactions and changes in data. By leveraging reactive data objects and composition functions, developers can create reactive components that automatically update in response to changes in their underlying data, providing a seamless and intuitive user experience.
|
||||
|
||||
Vue 3's enhanced performance optimizations, including better tree shaking and optimized reactivity tracking, further contribute to the creation of fast and responsive user interfaces. With these improvements, Vue 3 enables developers to build web applications that not only look great but also perform exceptionally well across a wide range of devices and screen sizes.
|
||||
|
||||
## Conclusion
|
||||
|
||||
In conclusion, Vue 3 represents a significant milestone in the evolution of Vue.js, offering developers a powerful and intuitive framework for building dynamic and responsive user interfaces. By leveraging Vue 3's reactivity system and Composition API, developers can create highly interactive and performant web applications that push the boundaries of what's possible on the web. Whether you're a seasoned Vue.js developer or just getting started, Vue 3 provides the tools and capabilities you need to bring your web projects to life in exciting new ways.
|
54
src/content/config.ts
Normal file
54
src/content/config.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
import { defineCollection, z } from 'astro:content'
|
||||
|
||||
const postsSchema = z.object({
|
||||
title: z.string(),
|
||||
description: z.string().optional(),
|
||||
duration: z.string().optional(),
|
||||
image: z
|
||||
.object({
|
||||
src: z.string(),
|
||||
alt: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
date: z
|
||||
.string()
|
||||
.or(z.date())
|
||||
.transform((val: string | number | Date) => new Date(val).toLocaleDateString('en-us', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})),
|
||||
draft: z.boolean().optional().default(false),
|
||||
lang: z.string().optional().default('en-US'),
|
||||
tag: z.string().optional(),
|
||||
redirect: z.string().optional(),
|
||||
video: z.boolean().optional(),
|
||||
recording: z.boolean().optional(),
|
||||
})
|
||||
|
||||
const pages = defineCollection({
|
||||
schema: z.object({
|
||||
title: z.string(),
|
||||
description: z.string().optional(),
|
||||
image: z
|
||||
.object({
|
||||
src: z.string(),
|
||||
alt: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
}),
|
||||
})
|
||||
|
||||
const blog = defineCollection({
|
||||
schema: postsSchema,
|
||||
})
|
||||
|
||||
const notes = defineCollection({
|
||||
schema: postsSchema,
|
||||
})
|
||||
|
||||
const reading = defineCollection({
|
||||
schema: postsSchema,
|
||||
})
|
||||
|
||||
export const collections = { pages, blog, notes, reading }
|
75
src/content/notes/astro.md
Normal file
75
src/content/notes/astro.md
Normal file
|
@ -0,0 +1,75 @@
|
|||
---
|
||||
title: Notes on Using Astro
|
||||
description: A New Experience in Building Modern Static Websites.
|
||||
duration: "5min"
|
||||
date: "2024-03-02"
|
||||
---
|
||||
|
||||
In the modern web development field, building fast, reliable, and easy-to-maintain static websites is one of the common challenges for developers. Traditional static site generators often face limitations such as lack of support for modern JavaScript frameworks, slow build times, or complex configuration files. However, with the emergence of Astro, these issues are becoming a thing of the past. Below are some of my experiences and notes from using Astro:
|
||||
|
||||
## Simplified Development Experience
|
||||
|
||||
Astro provides a simple yet powerful development experience, making it easy to build static websites. With its concise syntax and intuitive API, developers can quickly create highly optimized static websites without delving into complex configuration files or build pipelines. Astro's design philosophy is "out of the box," providing developers with a set of ready-to-use tools and best practices to accelerate the development process and reduce the learning curve.
|
||||
|
||||
Example Code:
|
||||
|
||||
```astro
|
||||
---
|
||||
import { React, Astro } from 'astro'
|
||||
|
||||
const Index = () => (
|
||||
<Astro>
|
||||
<h1>Hello, Astro!</h1>
|
||||
<p>Welcome to my Astro-powered website.</p>
|
||||
</Astro>
|
||||
)
|
||||
|
||||
export default Index
|
||||
---
|
||||
```
|
||||
|
||||
## Support for Multiple Frontend Frameworks
|
||||
|
||||
One notable feature is that Astro supports multiple frontend frameworks, including React, Vue.js, Svelte, and more. This means developers can choose their preferred framework to build websites without worrying about compatibility or performance issues. Astro provides dedicated plugins for integration with each framework, ensuring developers can leverage the full capabilities of the framework while benefiting from the advantages of Astro.
|
||||
|
||||
Example Code:
|
||||
|
||||
```astro
|
||||
---
|
||||
import { Vue, Astro } from 'astro'
|
||||
|
||||
const Index = () => (
|
||||
<Astro>
|
||||
<Vue>
|
||||
<template>
|
||||
<h1>Hello, Astro!</h1>
|
||||
<p>Welcome to my Astro-powered website.</p>
|
||||
</template>
|
||||
</Vue>
|
||||
</Astro>
|
||||
)
|
||||
|
||||
export default Index
|
||||
---
|
||||
```
|
||||
|
||||
## Instant Reload and Pre-rendering
|
||||
|
||||
Astro provides features like instant reload and pre-rendering, making the development process more efficient. Instant reload reflects code changes in real-time and updates them immediately in the browser, speeding up the development and debugging process. Pre-rendering generates static HTML during the build process, improving website performance and search engine optimization (SEO), resulting in better loading speed and user experience.
|
||||
|
||||
Example Code:
|
||||
|
||||
```astro
|
||||
---
|
||||
import { React, Astro } from 'astro'
|
||||
|
||||
const Index = () => (
|
||||
<Astro>
|
||||
<h1>Hello, Astro!</h1>
|
||||
<p>Welcome to my Astro-powered website.</p>
|
||||
</Astro>
|
||||
)
|
||||
|
||||
export default Index
|
||||
---
|
||||
```
|
19
src/content/pages/about.md
Normal file
19
src/content/pages/about.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
title: Unleash the Power of Web Development with Vitesse Theme for Astro
|
||||
---
|
||||
|
||||
![About Image](/about.jpg)
|
||||
|
||||
In the ever-evolving landscape of web development, staying ahead requires harnessing cutting-edge tools and frameworks that streamline workflows and empower developers to unleash their creativity. Enter Vitesse Theme for Astro: a game-changing template that combines the best of Vue.js, Unocss, and modern design principles to revolutionize frontend development.
|
||||
|
||||
At its core, Vitesse Theme for Astro is all about efficiency without sacrificing aesthetics. Inspired by the sleek design of antfu.me, this template offers a visually stunning experience while ensuring seamless integration with Vue.js and Unocss. Whether you're building a personal blog, a portfolio, or a business website, Vitesse Theme for Astro provides the flexibility and versatility to bring your vision to life.
|
||||
|
||||
One of the standout features of Vitesse Theme for Astro is its robust support for Vue.js. With Vue.js, developers can build dynamic and interactive web applications with ease. From reactive components to state management, Vue.js empowers developers to create sophisticated user interfaces that delight users and elevate the overall user experience.
|
||||
|
||||
But Vitesse Theme for Astro doesn't stop there. It also leverages the power of Unocss, a utility-first CSS framework that simplifies styling and ensures consistency across your project. With Unocss, developers can write less code while achieving greater flexibility and scalability, resulting in cleaner, more maintainable codebases.
|
||||
|
||||
Beyond its technical prowess, Vitesse Theme for Astro stands out for its sleek and modern design. Every element is carefully crafted to ensure a seamless and intuitive user experience, from crisp typography to smooth animations. Whether you're a seasoned developer or just getting started, Vitesse Theme for Astro makes it easy to create stunning web applications that captivate and engage your audience.
|
||||
|
||||
But perhaps the most compelling aspect of Vitesse Theme for Astro is its commitment to community-driven development. Built on open-source principles, Vitesse Theme for Astro welcomes contributions from developers around the world, ensuring that it continues to evolve and improve over time. Whether it's submitting bug fixes, suggesting new features, or sharing best practices, the Vitesse Theme for Astro community is vibrant and inclusive, fostering collaboration and innovation at every turn.
|
||||
|
||||
In conclusion, Vitesse Theme for Astro is more than just a template—it's a catalyst for innovation in frontend development. By combining the power of Vue.js, Unocss, and modern design principles, Vitesse Theme for Astro empowers developers to build sleek, responsive, and visually stunning web applications that push the boundaries of what's possible on the web.
|
17
src/content/pages/sponsor.md
Normal file
17
src/content/pages/sponsor.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
title: 'Unlocking Potential with Sponsorship: A Key Element of Vitesse Theme for Astro'
|
||||
---
|
||||
|
||||
In the dynamic world of web development, sponsorship plays a crucial role in fueling innovation, fostering collaboration, and supporting the growth of open-source projects. At the heart of Vitesse Theme for Astro lies a commitment to community-driven development, and sponsorship stands as a cornerstone of this ethos.
|
||||
|
||||
Sponsorship is not merely a transactional relationship; it's a partnership built on mutual trust and shared goals. Through sponsorship, individuals and organizations have the opportunity to invest in the sustainability and advancement of projects like Vitesse Theme for Astro, ensuring their longevity and continued evolution.
|
||||
|
||||
For sponsors, Vitesse Theme for Astro offers a platform to showcase their commitment to the developer community while gaining visibility among a diverse audience of frontend enthusiasts, Vue.js practitioners, and Unocss aficionados. By supporting Vitesse Theme for Astro, sponsors align themselves with a project that embodies innovation, accessibility, and excellence in frontend development.
|
||||
|
||||
In return for their support, sponsors of Vitesse Theme for Astro gain access to a range of exclusive benefits tailored to their needs and objectives. These benefits may include prominent placement on the project's website, recognition in documentation and release notes, invitations to participate in development discussions, and priority support from the project maintainers.
|
||||
|
||||
Furthermore, sponsorship of Vitesse Theme for Astro is an investment in the future of web development. By contributing financial resources, sponsors enable the project maintainers to dedicate more time and resources to improving the framework, addressing bugs, implementing new features, and providing ongoing support to the community.
|
||||
|
||||
Sponsorship is also a means of giving back to the ecosystem that supports and sustains developers around the world. By sponsoring Vitesse Theme for Astro, individuals and organizations demonstrate their commitment to nurturing a thriving and inclusive community of developers, where knowledge-sharing, collaboration, and innovation thrive.
|
||||
|
||||
In conclusion, sponsorship is a vital component of the ecosystem surrounding Vitesse Theme for Astro. It empowers individuals and organizations to play an active role in shaping the future of frontend development while reaping the benefits of increased visibility, collaboration opportunities, and a stronger, more sustainable open-source community.
|
34
src/content/reading/moon-and-sixpence.md
Normal file
34
src/content/reading/moon-and-sixpence.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
title: "The Moon and Sixpence"
|
||||
description: "人世漫长得转瞬即逝, 有人见尘埃, 有人见星辰。查尔斯就是那个终其一生在追逐星辰的人。"
|
||||
duration: "12min"
|
||||
date: "2023-09-22"
|
||||
lang: "zh-cn"
|
||||
draft: true
|
||||
---
|
||||
|
||||
"The Moon and Sixpence" is a captivating novel written by W. Somerset Maugham, first published in 1919. This compelling narrative delves into the life of Charles Strickland, a middle-aged English stockbroker who abandons his family and comfortable life in London to pursue his passion for painting in Paris. Inspired by the life of Paul Gauguin, Maugham crafts a mesmerizing tale exploring the complexities of art, passion, and the pursuit of one's true calling.
|
||||
|
||||
## Plot Overview:
|
||||
|
||||
The story unfolds through the eyes of the narrator, who is fascinated by the enigmatic Strickland. Despite being outwardly unremarkable, Strickland possesses an inner fire that drives him to forsake societal norms and follow his artistic ambitions. As the narrator delves deeper into Strickland's past, he uncovers a tale of obsession, sacrifice, and ultimately, redemption.
|
||||
|
||||
## Themes and Analysis:
|
||||
|
||||
One of the central themes of "The Moon and Sixpence" is the conflict between artistic genius and societal conventions. Strickland's decision to abandon his family and pursue art shocks those around him, who view his actions as selfish and irresponsible. However, Maugham challenges the reader to question whether true artistic greatness can coexist with societal expectations.
|
||||
|
||||
The character of Strickland is portrayed as a complex and contradictory figure. On one hand, he is ruthless in his pursuit of artistic perfection, willing to sacrifice everything in its pursuit. Yet, he is also portrayed as a deeply flawed individual, capable of callousness and cruelty towards those who love him. Through Strickland's character, Maugham explores the price of artistic genius and the impact it can have on both the artist and those around them.
|
||||
|
||||
Another prominent theme in the novel is the clash between Western civilization and the exotic allure of the South Pacific. Like Gauguin, Strickland is drawn to the primitive beauty of Tahiti, where he seeks inspiration for his paintings. However, his idealized vision of the South Pacific is shattered by the harsh realities of life on the island, leading to a poignant exploration of cultural imperialism and the search for authenticity in art.
|
||||
|
||||
## Character Development:
|
||||
|
||||
Maugham's characterizations are rich and nuanced, particularly in his portrayal of Strickland. Despite his flaws, Strickland is depicted as a figure of singular vision and uncompromising integrity. His refusal to conform to societal expectations marks him as a rebel and a visionary, willing to sacrifice everything for his art. Similarly, the supporting characters in the novel are equally well-drawn, each contributing to the unfolding drama in their own unique way.
|
||||
|
||||
## Writing Style:
|
||||
|
||||
Maugham's prose is elegant and evocative, capturing the beauty and brutality of Strickland's world with equal precision. His descriptions of Parisian cafes, Tahitian landscapes, and the inner workings of the human psyche are rendered with a keen eye for detail and an unparalleled sense of atmosphere. The result is a novel that immerses the reader in its world from the very first page, inviting them to contemplate the mysteries of art and existence alongside its unforgettable characters.
|
||||
|
||||
## Conclusion:
|
||||
|
||||
In conclusion, "The Moon and Sixpence" is a masterpiece of modern literature that continues to resonate with readers over a century after its initial publication. Through its exploration of art, passion, and the human condition, Maugham crafts a timeless tale that challenges our assumptions about creativity, morality, and the nature of genius. Whether you're an art enthusiast, a lover of literary fiction, or simply in search of a compelling story, "The Moon and Sixpence" is a novel that will stay with you long after you've turned the final page.
|
2
src/env.d.ts
vendored
Normal file
2
src/env.d.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
37
src/layouts/BaseLayout.astro
Normal file
37
src/layouts/BaseLayout.astro
Normal file
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
import { ViewTransitions } from 'astro:transitions'
|
||||
import BaseHead from '../components/BaseHead.astro'
|
||||
import Header from '../components/Header.vue'
|
||||
import Footer from '../components/Footer.vue'
|
||||
import PageNav from '../components/PageNav.vue'
|
||||
import PageOperate from '../components/PageOperate.vue'
|
||||
|
||||
const { pageNav = false, pageOperate = false, ...head } = Astro.props
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<BaseHead {...head} />
|
||||
<ViewTransitions />
|
||||
</head>
|
||||
<body class="bg-main text-main min-h-screen font-sans w-full">
|
||||
<Header client:load />
|
||||
<main class="grow max-w-3xl mx-auto py-10 px-6">
|
||||
{pageNav && <PageNav client:load pathname={Astro.url.pathname} />}
|
||||
<slot />
|
||||
{
|
||||
pageOperate && (
|
||||
<div mt-8>
|
||||
<PageOperate
|
||||
client:load
|
||||
showShare={head.pageType === 'article'}
|
||||
url={Astro.url}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
37
src/pages/[...slug].astro
Normal file
37
src/pages/[...slug].astro
Normal file
|
@ -0,0 +1,37 @@
|
|||
---
|
||||
import { getCollection } from 'astro:content'
|
||||
import BaseLayout from '../layouts/BaseLayout.astro'
|
||||
import type { CollectionPages } from '../types'
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const pages = await getCollection('pages')
|
||||
return pages.map((page) => {
|
||||
return {
|
||||
params: { slug: page.slug },
|
||||
props: { page },
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type Props = { page: CollectionPages }
|
||||
|
||||
const { page } = Astro.props
|
||||
const { title, description, image } = page.data
|
||||
const { Content } = await page.render()
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title={title}
|
||||
description={description}
|
||||
image={image}
|
||||
pageOperate={true}
|
||||
>
|
||||
<article class="mb-16 sm:mb-24">
|
||||
<h1 class="text-title">
|
||||
{title}
|
||||
</h1>
|
||||
<div class="max-w-none prose">
|
||||
<Content />
|
||||
</div>
|
||||
</article>
|
||||
</BaseLayout>
|
16
src/pages/blog.astro
Normal file
16
src/pages/blog.astro
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro'
|
||||
import ListPosts from '../components/ListPosts.vue'
|
||||
import { getPosts } from '../utils/posts'
|
||||
|
||||
const posts = await getPosts('blog')
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="Blog"
|
||||
description="List of all the blog posts."
|
||||
pageNav={true}
|
||||
pageOperate={true}
|
||||
>
|
||||
<ListPosts list={posts} />
|
||||
</BaseLayout>
|
55
src/pages/index.astro
Normal file
55
src/pages/index.astro
Normal file
|
@ -0,0 +1,55 @@
|
|||
---
|
||||
import { marked } from 'marked'
|
||||
import BaseLayout from '../layouts/BaseLayout.astro'
|
||||
import siteConfig from '../site-config'
|
||||
|
||||
const hero = siteConfig.hero
|
||||
---
|
||||
|
||||
<BaseLayout description={siteConfig.description} image={siteConfig.image}>
|
||||
{
|
||||
(hero.title || hero.image.src || hero.text || hero.socialLinks) && (
|
||||
<div class="prose max-w-3xl w-full">
|
||||
{hero.title && <h1 class="text-title">{hero.title}</h1>}
|
||||
{hero.image.src && (
|
||||
<img
|
||||
class="w-full"
|
||||
src={hero.image.src}
|
||||
loading="lazy"
|
||||
decoding="async"
|
||||
alt={hero.image.alt || 'Hero Image'}
|
||||
/>
|
||||
)}
|
||||
{hero.text && <div set:html={marked.parse(hero.text)} />}
|
||||
{hero.socialLinks.length > 0 && (
|
||||
<>
|
||||
<hr class="w-14 mx-auto my-8 border-t border-truegray-200 dark:border-truegray-800" />
|
||||
<p>Find me on</p>
|
||||
<p class="flex gap-x-4 gap-y-2 flex-wrap">
|
||||
{hero.socialLinks.map((link) => (
|
||||
<a
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="prose-link flex items-center"
|
||||
>
|
||||
<i class:list={[link.icon, 'text-sm mr-1']} />
|
||||
<span>{link.text}</span>
|
||||
</a>
|
||||
))}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
{siteConfig.email && (
|
||||
<p>
|
||||
If you have any questions, please email me at
|
||||
<a prose-link href={`mailto:${siteConfig.email}`}>
|
||||
{siteConfig.email}
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</BaseLayout>
|
16
src/pages/notes.astro
Normal file
16
src/pages/notes.astro
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro'
|
||||
import ListPosts from '../components/ListPosts.vue'
|
||||
import { getPosts } from '../utils/posts'
|
||||
|
||||
const posts = await getPosts('notes')
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="Notes"
|
||||
description="List of all the note posts."
|
||||
pageNav={true}
|
||||
pageOperate={true}
|
||||
>
|
||||
<ListPosts list={posts} />
|
||||
</BaseLayout>
|
41
src/pages/posts/[slug].astro
Normal file
41
src/pages/posts/[slug].astro
Normal file
|
@ -0,0 +1,41 @@
|
|||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro'
|
||||
import { type CollectionPosts } from '../../types'
|
||||
import { getAllPosts } from '../../utils/posts'
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getAllPosts()
|
||||
|
||||
return posts.map((post) => ({
|
||||
params: { slug: post.slug },
|
||||
props: {
|
||||
post,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
type Props = { post: CollectionPosts }
|
||||
|
||||
const { post } = Astro.props
|
||||
const { title, image, description } = post.data
|
||||
const { Content } = await post.render()
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title={title}
|
||||
description={description}
|
||||
image={image}
|
||||
pageType="article"
|
||||
pageOperate={true}
|
||||
>
|
||||
<article class="mb-16 sm:mb-24">
|
||||
<header class="mb-8">
|
||||
<h1 class="text-title">
|
||||
{title}
|
||||
</h1>
|
||||
</header>
|
||||
<div class="max-w-none prose">
|
||||
<Content />
|
||||
</div>
|
||||
</article>
|
||||
</BaseLayout>
|
27
src/pages/projects.astro
Normal file
27
src/pages/projects.astro
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
import siteConfig from '../site-config'
|
||||
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>
|
||||
{
|
||||
siteConfig.projects.length > 0 && (
|
||||
<div>
|
||||
{siteConfig.projects.map((group) => (
|
||||
<div mb-10>
|
||||
<h2 mb-4 font-bold>
|
||||
{group.title}
|
||||
</h2>
|
||||
<ListProjects list={group.projects} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</BaseLayout>
|
16
src/pages/reading.astro
Normal file
16
src/pages/reading.astro
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro'
|
||||
import ListPosts from '../components/ListPosts.vue'
|
||||
import { getPosts } from '../utils/posts'
|
||||
|
||||
const posts = await getPosts('reading')
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="Reading"
|
||||
description="List of all the reading posts."
|
||||
pageNav={true}
|
||||
pageOperate={true}
|
||||
>
|
||||
<ListPosts list={posts} />
|
||||
</BaseLayout>
|
22
src/pages/rss.xml.js
Normal file
22
src/pages/rss.xml.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import rss from '@astrojs/rss'
|
||||
import siteConfig from '../site-config'
|
||||
import { getAllPosts } from '../utils/posts'
|
||||
|
||||
export async function GET(context) {
|
||||
const posts = await getAllPosts()
|
||||
|
||||
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}>`,
|
||||
}
|
||||
}),
|
||||
})
|
||||
}
|
16
src/pages/talks.astro
Normal file
16
src/pages/talks.astro
Normal file
|
@ -0,0 +1,16 @@
|
|||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro'
|
||||
import ListPosts from '../components/ListPosts.vue'
|
||||
import { getPosts } from '../utils/posts'
|
||||
|
||||
const posts = await getPosts('talks')
|
||||
---
|
||||
|
||||
<BaseLayout
|
||||
title="Talks"
|
||||
description="List of all the talk posts."
|
||||
pageNav={true}
|
||||
pageOperate={true}
|
||||
>
|
||||
<ListPosts list={posts} />
|
||||
</BaseLayout>
|
131
src/site-config.ts
Normal file
131
src/site-config.ts
Normal file
|
@ -0,0 +1,131 @@
|
|||
export const siteConfig = {
|
||||
author: 'Kaivan Wong',
|
||||
title: 'Vitesse theme for Astro',
|
||||
subtitle: 'Supports Vue and UnoCSS.',
|
||||
description: 'Vitesse theme for Astro blog, supports Vue and UnoCSS.',
|
||||
image: {
|
||||
src: '/preview.jpg',
|
||||
alt: 'Vitesse theme for Astro - Supports Vue and UnoCSS.',
|
||||
},
|
||||
email: '',
|
||||
headerNavLinks: [
|
||||
{
|
||||
text: 'Home',
|
||||
href: '/',
|
||||
},
|
||||
{
|
||||
text: 'Blog',
|
||||
href: '/blog',
|
||||
},
|
||||
{
|
||||
text: 'Projects',
|
||||
href: '/projects',
|
||||
},
|
||||
],
|
||||
hero: {
|
||||
title: 'Welcome to Vitesse Theme for Astro',
|
||||
text: `<p>Experience the perfect blend of efficiency and aesthetics with Vitesse Theme for Astro. Inspired by the sleek design of antfu.me, this template seamlessly integrates Vue and Unocss to provide you with a cutting-edge development experience.</p>
|
||||
<p><b>Key Features:</b></p>
|
||||
<ol>
|
||||
<li><b>Vue Support:</b> Harness the power of Vue.js to build dynamic and interactive web applications. Vitesse Theme for Astro ensures smooth integration and efficient utilization of Vue components for enhanced functionality.</li>
|
||||
<li><b>Unocss Integration:</b> Streamline your styling process with Unocss, a utility-first CSS framework. By utilizing only the styles you need, Unocss optimizes your codebase for performance without compromising on design flexibility.</li>
|
||||
<li><b>Sleek Design:</b> Drawing inspiration from the modern aesthetic of antfu.me, Vitesse Theme for Astro offers a clean and visually appealing design. From crisp typography to intuitive layouts, every element is crafted with attention to detail to elevate your web presence.</li>
|
||||
<li><b>Customizable Components:</b> Tailor your web applications to suit your unique requirements with Vitesse Theme's customizable components. Whether you're building a portfolio, blog, or e-commerce site, our flexible components adapt to your needs with ease.</li>
|
||||
<li><b>Performance Optimization:</b> Deliver lightning-fast user experiences with Vitesse Theme for Astro's focus on performance optimization. By minimizing unnecessary bloat and prioritizing efficient code practices, your applications will load swiftly and operate seamlessly across devices.</li>
|
||||
</ol>
|
||||
<p>Elevate your web development journey with Vitesse Theme for Astro. Experience the perfect synergy of Vue, Unocss, and modern design principles to create stunning web applications that captivate and engage your audience.</p>
|
||||
`,
|
||||
image: {
|
||||
src: 'hero.jpg',
|
||||
alt: '',
|
||||
},
|
||||
socialLinks: [],
|
||||
},
|
||||
pageNavLinks: [
|
||||
{
|
||||
text: 'Blog',
|
||||
href: '/blog',
|
||||
},
|
||||
{
|
||||
text: 'Notes',
|
||||
href: '/notes',
|
||||
},
|
||||
{
|
||||
text: 'Reading',
|
||||
href: '/reading',
|
||||
},
|
||||
],
|
||||
projects: [
|
||||
{
|
||||
title: 'Develop Templates',
|
||||
projects: [
|
||||
{
|
||||
text: 'Frosty Web',
|
||||
description: 'A clean and minimalist website template designed to showcase content with style.',
|
||||
icon: 'i-carbon-webhook',
|
||||
href: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Framework',
|
||||
projects: [
|
||||
{
|
||||
text: 'Pixel Craft',
|
||||
description: 'Frontend framework for crafting pixel-perfect web applications with a responsive design.',
|
||||
icon: 'i-carbon-pen-fountain',
|
||||
href: '',
|
||||
},
|
||||
{
|
||||
text: 'Aurora UI',
|
||||
description: 'Modern UI library designed to streamline frontend development with modular components.',
|
||||
icon: 'i-carbon-mountain',
|
||||
href: '',
|
||||
},
|
||||
{
|
||||
text: 'Nimbus CSS',
|
||||
description: 'Lightweight CSS framework for building responsive websites with a flexible grid system.',
|
||||
icon: 'i-carbon-face-satisfied',
|
||||
href: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Library',
|
||||
projects: [
|
||||
{
|
||||
text: 'Zenith Scroll',
|
||||
description: 'Smooth-scrolling JavaScript library for creating immersive scrolling experiences.',
|
||||
icon: '',
|
||||
href: '',
|
||||
},
|
||||
{
|
||||
text: 'Polaris JS',
|
||||
description: 'Lightweight JavaScript library for creating smooth animations and transitions.',
|
||||
icon: 'i-carbon-tools-alt',
|
||||
href: '',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
footerNavLinks: [
|
||||
{
|
||||
text: 'About',
|
||||
href: '/about',
|
||||
},
|
||||
{
|
||||
text: 'Sponsor',
|
||||
href: '/sponsor',
|
||||
},
|
||||
{
|
||||
text: 'Contact Me',
|
||||
href: 'mailto:kaivanwong@outlook.me',
|
||||
},
|
||||
{
|
||||
text: 'Github Repo',
|
||||
href: 'https://github.com/kaivanwong/vitesse-astro-theme',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default siteConfig
|
12
src/styles/markdown.css
Normal file
12
src/styles/markdown.css
Normal file
|
@ -0,0 +1,12 @@
|
|||
html.dark .astro-code,
|
||||
html.dark .astro-code span {
|
||||
color: var(--shiki-dark) !important;
|
||||
background-color: var(--shiki-dark-bg) !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;
|
||||
}
|
||||
|
||||
.prose a {
|
||||
--at-apply: prose-link;
|
||||
}
|
9
src/types.ts
Normal file
9
src/types.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import type { CollectionEntry } from 'astro:content'
|
||||
|
||||
export type Posts = 'blog' | 'notes' | 'reading'
|
||||
|
||||
export type CollectionPosts = CollectionEntry<Posts>
|
||||
|
||||
export type Pages = 'pages'
|
||||
|
||||
export type CollectionPages = CollectionEntry<Pages>
|
22
src/utils/posts.ts
Normal file
22
src/utils/posts.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { getCollection } from 'astro:content'
|
||||
import type { CollectionPosts, Posts } from '../types'
|
||||
|
||||
export function sortPostsByDate(itemA: CollectionPosts, itemB: CollectionPosts) {
|
||||
return new Date(itemB.data.date).getTime() - new Date(itemA.data.date).getTime()
|
||||
}
|
||||
|
||||
export async function getPosts(type: Posts) {
|
||||
return (await getCollection(type, ({ data }) => {
|
||||
return import.meta.env.PROD ? data.draft !== true : true
|
||||
})).sort(sortPostsByDate)
|
||||
}
|
||||
|
||||
export async function getAllPosts() {
|
||||
const posts = await Promise.all([
|
||||
getPosts('blog'),
|
||||
getPosts('notes'),
|
||||
getPosts('reading'),
|
||||
getPosts('talks'),
|
||||
])
|
||||
return posts.flat().sort(sortPostsByDate)
|
||||
}
|
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"strictNullChecks": true
|
||||
}
|
||||
}
|
53
uno.config.ts
Normal file
53
uno.config.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import {
|
||||
defineConfig,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
presetTypography,
|
||||
presetUno,
|
||||
presetWebFonts,
|
||||
transformerDirectives,
|
||||
transformerVariantGroup,
|
||||
} from 'unocss'
|
||||
|
||||
export default defineConfig({
|
||||
shortcuts: [
|
||||
{
|
||||
'bg-main': 'bg-white dark:bg-black',
|
||||
'text-main': 'text-hex-555 dark:text-hex-bbb',
|
||||
'text-link': 'text-dark dark:text-white ',
|
||||
'border-main': 'border-truegray-300 dark:border-truegray-600',
|
||||
},
|
||||
{
|
||||
'text-title': 'text-link text-4xl font-800',
|
||||
'nav-link': 'text-link opacity-70 hover:opacity-100 transition-opacity duration-200 cursor-pointer',
|
||||
'prose-link': 'text-link text-nowrap cursor-pointer border-b-1 !border-opacity-30 hover:!border-opacity-100 border-neutral-500 hover:border-truegray-600 dark:border-neutral-500 hover:dark:border-truegray-400 transition-border-color duration-200 decoration-none',
|
||||
'container-link': 'p-2 opacity-60 hover:opacity-100 cursor-pointer hover:bg-truegray-500 !bg-opacity-10 transition-colors transition-opacity duration-200',
|
||||
},
|
||||
],
|
||||
presets: [
|
||||
presetUno(),
|
||||
presetAttributify(),
|
||||
presetIcons({
|
||||
scale: 1.2,
|
||||
prefix: 'i-',
|
||||
extraProperties: {
|
||||
display: 'inline-block',
|
||||
},
|
||||
}),
|
||||
presetTypography(),
|
||||
presetWebFonts({
|
||||
fonts: {
|
||||
sans: 'Inter:400,600,800',
|
||||
mono: 'DM Mono:400,600',
|
||||
},
|
||||
}),
|
||||
],
|
||||
transformers: [transformerDirectives(), transformerVariantGroup()],
|
||||
safelist: [
|
||||
'i-carbon-webhook',
|
||||
'i-carbon-mountain',
|
||||
'i-carbon-pen-fountain',
|
||||
'i-carbon-face-satisfied',
|
||||
'i-carbon-tools-alt',
|
||||
],
|
||||
})
|
Loading…
Reference in a new issue