Refactor
This commit is contained in:
@@ -1,12 +0,0 @@
|
||||
# EditorConfig is awesome: https://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = true
|
||||
@@ -1,20 +0,0 @@
|
||||
/* eslint-env node */
|
||||
require("@rushstack/eslint-patch/modern-module-resolution");
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
"plugin:vue/vue3-essential",
|
||||
"eslint:recommended",
|
||||
"@vue/eslint-config-typescript/recommended",
|
||||
"@vue/eslint-config-prettier",
|
||||
],
|
||||
env: {
|
||||
node: true,
|
||||
"vue/setup-compiler-macros": true,
|
||||
},
|
||||
rules: {
|
||||
"vue/multi-word-component-names": 0,
|
||||
"@typescript-eslint/ban-ts-comment": 0,
|
||||
},
|
||||
};
|
||||
+17
-22
@@ -1,29 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
histoire-dist
|
||||
coverage
|
||||
*.local
|
||||
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
# jetbrains setting folder
|
||||
.idea/
|
||||
@@ -1,2 +0,0 @@
|
||||
# Expose Astro dependencies for `pnpm` users
|
||||
shamefully-hoist=true
|
||||
@@ -0,0 +1,3 @@
|
||||
templates/
|
||||
pnpm-lock.yaml
|
||||
pnpm-workspace.yaml
|
||||
Vendored
-11
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"command": "./node_modules/.bin/astro dev",
|
||||
"name": "Development server",
|
||||
"request": "launch",
|
||||
"type": "node-terminal"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"emmet.includeLanguages": {
|
||||
"astro": "html"
|
||||
},
|
||||
"i18n-ally.localesPaths": ["demo-themes/theme-earth/i18n"]
|
||||
}
|
||||
@@ -1,60 +1 @@
|
||||
# theme-astro-starter(Experimental)
|
||||
|
||||
Halo 2.0 基于 [Astro](https://astro.build) 和 [Thymeleaf](https://www.thymeleaf.org/) 的主题快速开始模板。
|
||||
|
||||
> 目前还处于实验和探索的阶段。
|
||||
|
||||
## 原理
|
||||
|
||||
1. Astro 作为 SSG 渲染框架,用于组件封装、页面框架组织、静态页面生成、静态资源的构建。
|
||||
2. Thymeleaf 作为后端页面渲染框架,并且负责提供数据进行页面数据的渲染。由于 Thymeleaf 的语法是基于 HTML 元素的 Attributes 扩展,所以理论上并不会破坏 HTML 的结构。那么就可以在 Astro 组件中使用 Halo 提供的数据和页面进行绑定。这样就实现了在开发环境并不依赖于 Halo 后端,可以在开发过程中保留 hmr。并且可以做到在构建之后可以直接给 Halo 使用。
|
||||
|
||||
## 特性
|
||||
|
||||
1. 得益于 Astro 框架,可以使用现代的前端技术栈开发页面。包括但不限于 Vue React Svelte 等,意味着可以使用这些现代前端框架进行组件开发。
|
||||
2. 在开发环境可以实现 hmr。
|
||||
3. 在构建页面之后,会自动处理构建产物的资源地址,转换为 Thymeleaf 可识别的语法。
|
||||
4. 最终构建的产物可通过 HTML 直接访问,也可以部署在任何静态页面服务器,可作为页面预览原型。但因为没有经过 Halo 进行数据绑定,所以没有数据填充。
|
||||
|
||||
## 开发
|
||||
|
||||
项目结构:
|
||||
|
||||
```text
|
||||
├── astro.config.mjs Astro 配置文件
|
||||
├── package.json
|
||||
├── pnpm-lock.yaml
|
||||
├── src
|
||||
│ ├── components
|
||||
│ │ ├── Card.astro
|
||||
│ │ └── PostCard.vue Vue 3 组件
|
||||
│ ├── layouts Astro 布局组件
|
||||
│ │ └── Layout.astro
|
||||
│ └── pages
|
||||
│ ├── index.astro Astro 页面组件
|
||||
│ └── post.astro
|
||||
├── templates
|
||||
│ ├── assets bundle 之后的静态资源文件
|
||||
│ │ ├── 4669d51c.dbc606a6.css
|
||||
│ │ ├── PostCard.42ac9187.js
|
||||
│ │ ├── chunks
|
||||
│ │ │ └── runtime-core.esm-bundler.e59bb94c.js
|
||||
│ │ └── client.e67fc49c.js
|
||||
│ ├── favicon.ico
|
||||
│ ├── index.html 构建之后的静态页面,作为 Thymeleaf 渲染模板
|
||||
│ └── post.html
|
||||
├── theme.yaml 主题描述文件
|
||||
└── tsconfig.json
|
||||
```
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ruibaby/theme-astro-starter
|
||||
|
||||
pnpm install
|
||||
|
||||
# 启动 Vite 开发环境
|
||||
pnpm dev
|
||||
|
||||
# 构建最终产物,构建之后可以直接在 Halo 当中安装使用
|
||||
pnpm build
|
||||
```
|
||||
WIP
|
||||
+7
-9
@@ -1,16 +1,14 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from "astro/config";
|
||||
import vue from "@astrojs/vue";
|
||||
import tailwind from "@astrojs/tailwind";
|
||||
import astroHaloThemeIntegration from "@halo-dev/astro-halo-theme-integration";
|
||||
|
||||
// https://astro.build/config
|
||||
import vue from "@astrojs/vue";
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [vue(), tailwind(), astroHaloThemeIntegration()],
|
||||
outDir: "./templates",
|
||||
base: "/themes/astro-starter",
|
||||
build: {
|
||||
assets: "assets",
|
||||
format: "file",
|
||||
},
|
||||
server: {
|
||||
port: 4000,
|
||||
},
|
||||
outDir: "./templates",
|
||||
integrations: [vue()],
|
||||
});
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
declare module "*.vue" {
|
||||
import type { DefineComponent } from "vue";
|
||||
// eslint-disable-next-line
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
foo=这是一段测试文本
|
||||
bar=这是另外一段测试文本
|
||||
hello=你好,{0}
|
||||
@@ -1,3 +0,0 @@
|
||||
foo=foo
|
||||
bar=bar
|
||||
hello=Hello, {0}
|
||||
@@ -1,3 +0,0 @@
|
||||
foo=这是一段测试文本
|
||||
bar=这是另外一段测试文本
|
||||
hello=你好,{0}
|
||||
+29
-22
@@ -1,31 +1,38 @@
|
||||
{
|
||||
"name": "@halo-dev/theme-astro-starter",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview"
|
||||
"preview": "astro preview",
|
||||
"astro": "astro",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/tailwind": "^1.0.0",
|
||||
"@astrojs/vue": "^1.0.0",
|
||||
"@halo-dev/astro-halo-theme-integration": "^0.0.1",
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@types/node": "16.11.47",
|
||||
"@vue/eslint-config-prettier": "^7.0.0",
|
||||
"@vue/eslint-config-typescript": "^11.0.0",
|
||||
"@vue/tsconfig": "^0.1.3",
|
||||
"astro": "^1.1.1",
|
||||
"eslint": "^8.23.0",
|
||||
"eslint-plugin-vue": "^9.4.0",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||
"vue": "^3.2.37"
|
||||
"prettier": {
|
||||
"plugins": [
|
||||
"prettier-plugin-astro"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.astro",
|
||||
"options": {
|
||||
"parser": "astro"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/vue": "^1.6.7",
|
||||
"dayjs": "^1.11.5"
|
||||
"@astrojs/mdx": "^5.0.3",
|
||||
"@astrojs/rss": "^4.0.18",
|
||||
"@astrojs/vue": "^6.0.1",
|
||||
"astro": "^6.1.4",
|
||||
"sharp": "^0.34.5",
|
||||
"vue": "^3.5.32"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "3.8.1",
|
||||
"prettier-plugin-astro": "0.14.1"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+5565
-3942
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
sharp: true
|
||||
@@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: [require("prettier-plugin-tailwindcss")],
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>About</title>
|
||||
</head>
|
||||
<body>
|
||||
<p
|
||||
th:text="没有经过 Astro 渲染,原生使用 Thymeleaf 语法。在构建时,将被 Copy 到 templates 目录。(Thymeleaf)"
|
||||
>
|
||||
没有经过 Astro 渲染,原生使用 Thymeleaf 语法。在构建时,将被 Copy 到
|
||||
templates 目录。(Native)
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -1,72 +0,0 @@
|
||||
apiVersion: v1alpha1
|
||||
kind: Setting
|
||||
metadata:
|
||||
name: theme-astro-starter-setting
|
||||
spec:
|
||||
- group: basic
|
||||
label: 基本设置
|
||||
formSchema:
|
||||
- $el: h2
|
||||
children: Welcome to Theme Astro Starter World
|
||||
- $formkit: select
|
||||
name: sidebar_width
|
||||
label: 侧边栏宽度
|
||||
options:
|
||||
'20': 20%
|
||||
'30': 30%
|
||||
'40': 40%
|
||||
'50': 50%
|
||||
- $formkit: text
|
||||
name: sidebar_background
|
||||
label: 侧边栏背景图
|
||||
- $formkit: text
|
||||
name: top_logo
|
||||
label: 左上角图标
|
||||
- $formkit: radio
|
||||
name: circle_avatar
|
||||
label: 原型头像
|
||||
options:
|
||||
- label: 开启
|
||||
value: true
|
||||
- label: 关闭
|
||||
value: false
|
||||
value: false
|
||||
- $formkit: radio
|
||||
name: katex
|
||||
label: KaTex 公式渲染
|
||||
options:
|
||||
- label: 开启
|
||||
value: true
|
||||
- label: 关闭
|
||||
value: false
|
||||
value: false
|
||||
- $formkit: textarea
|
||||
name: codeblock_highlight_languages
|
||||
label: 文章代码高亮语言
|
||||
help: >-
|
||||
默认已经包含:php,diff,java,css,bash,json,perl,swift,plaintext,ruby,yaml,sql,vbnet,ini,scss,less,cpp,typescript,csharp,rust,python,python-repl,objectivec,r,shell,makefile,go,xml,markdown,lua,javascript,php-template,kotlin,c
|
||||
如果需要添加其他语言,请按照相同格式添加。
|
||||
- group: social
|
||||
label: 社交资料
|
||||
formSchema:
|
||||
- $formkit: radio
|
||||
name: rss
|
||||
label: RSS
|
||||
options:
|
||||
- label: 开启
|
||||
value: true
|
||||
- label: 关闭
|
||||
value: false
|
||||
value: true
|
||||
- $formkit: text
|
||||
name: twitter
|
||||
label: Twitter
|
||||
- $formkit: text
|
||||
name: telegram
|
||||
label: Telegram
|
||||
- $formkit: text
|
||||
name: github
|
||||
label: GitHub
|
||||
- $formkit: text
|
||||
name: email
|
||||
label: Email
|
||||
@@ -1,74 +0,0 @@
|
||||
---
|
||||
export interface Props {
|
||||
title: string;
|
||||
body: string;
|
||||
href: string;
|
||||
}
|
||||
const { href, title, body } = Astro.props;
|
||||
---
|
||||
|
||||
<li class="link-card">
|
||||
<a href={href}>
|
||||
<h2>
|
||||
{title}
|
||||
<span>→</span>
|
||||
</h2>
|
||||
<p>
|
||||
{body}
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
<style>
|
||||
:root {
|
||||
--link-gradient: linear-gradient(45deg, #4f39fa, #da62c4 30%, var(--color-border) 60%);
|
||||
}
|
||||
|
||||
.link-card {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
padding: 0.15rem;
|
||||
background-image: var(--link-gradient);
|
||||
background-size: 400%;
|
||||
border-radius: 0.5rem;
|
||||
background-position: 100%;
|
||||
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.link-card>a {
|
||||
width: 100%;
|
||||
text-decoration: none;
|
||||
line-height: 1.4;
|
||||
padding: 1em 1.3em;
|
||||
border-radius: 0.35rem;
|
||||
color: var(--text-color);
|
||||
background-color: white;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0.75rem;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h2 span {
|
||||
display: inline-block;
|
||||
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
|
||||
.link-card:is(:hover, :focus-within) {
|
||||
background-position: 0;
|
||||
}
|
||||
|
||||
.link-card:is(:hover, :focus-within) h2 {
|
||||
color: #4f39fa;
|
||||
}
|
||||
|
||||
.link-card:is(:hover, :focus-within) h2 span {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
// TODO: Only execute when compiling Astro templates, so it will not be dynamically updated
|
||||
const today = new Date();
|
||||
---
|
||||
|
||||
<footer>
|
||||
<div class="footer-inner">
|
||||
<span>
|
||||
© {today.getFullYear()}
|
||||
<th:block th:text="${site.title}"></th:block>.
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<style>
|
||||
footer {
|
||||
border-top: 1px solid var(--rule);
|
||||
background: var(--bg);
|
||||
}
|
||||
|
||||
.footer-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
width: 820px;
|
||||
max-width: calc(100% - 2.5rem);
|
||||
margin: 0 auto;
|
||||
padding: 1.5rem 0 2.5rem;
|
||||
color: var(--ink-3);
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,101 @@
|
||||
---
|
||||
import ThemeSwitcher from "./ThemeSwitcher.vue";
|
||||
---
|
||||
|
||||
<header>
|
||||
<div class="header-inner">
|
||||
<a class="brand" href="/" th:text="${site.title}"></a>
|
||||
<nav
|
||||
th:with="menu = ${menuFinder.getPrimary()}"
|
||||
aria-label="Main navigation"
|
||||
>
|
||||
<a
|
||||
th:each="menuItem : ${menu.menuItems}"
|
||||
th:href="@{${menuItem.status.href}}"
|
||||
th:target="${menuItem.spec.target?.value}"
|
||||
th:text="${menuItem.status.displayName}"
|
||||
>
|
||||
</a>
|
||||
</nav>
|
||||
<ThemeSwitcher client:load />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style>
|
||||
header {
|
||||
background: var(--bg);
|
||||
border-bottom: 1px solid var(--rule);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.header-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 2rem;
|
||||
width: 820px;
|
||||
max-width: calc(100% - 2.5rem);
|
||||
margin: 0 auto;
|
||||
height: 58px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
color: var(--ink);
|
||||
text-decoration: none;
|
||||
letter-spacing: -0.02em;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.brand:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
nav :global(a) {
|
||||
display: inline-block;
|
||||
padding: 0.3rem 0.65rem;
|
||||
border-radius: 6px;
|
||||
font-size: 0.9rem;
|
||||
color: var(--ink-2);
|
||||
text-decoration: none;
|
||||
transition:
|
||||
color 0.15s ease,
|
||||
background 0.15s ease;
|
||||
}
|
||||
|
||||
nav :global(a:hover) {
|
||||
color: var(--ink);
|
||||
background: var(--bg-raised);
|
||||
}
|
||||
|
||||
nav :global(a.active) {
|
||||
color: var(--ink);
|
||||
background: var(--bg-raised);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@media (max-width: 680px) {
|
||||
.header-inner {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
height: auto;
|
||||
padding: 1rem 0;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
nav {
|
||||
flex-wrap: wrap;
|
||||
gap: 0.1rem;
|
||||
margin-left: -0.65rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,52 +0,0 @@
|
||||
<script lang="ts" setup>
|
||||
import { TransitionRoot } from "@headlessui/vue";
|
||||
import { ref } from "vue";
|
||||
|
||||
const isShowing = ref(true);
|
||||
|
||||
function resetIsShowing() {
|
||||
isShowing.value = false;
|
||||
|
||||
setTimeout(() => {
|
||||
isShowing.value = true;
|
||||
}, 500);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<span class="text-xl font-bold" th:text="@{post.title}">Hello Halo</span>
|
||||
|
||||
<div class="flex flex-col items-center py-16">
|
||||
<div class="h-32 w-32">
|
||||
<TransitionRoot
|
||||
:show="isShowing"
|
||||
appear
|
||||
as="template"
|
||||
enter="transform transition duration-[400ms]"
|
||||
enter-from="opacity-0 rotate-[-120deg] scale-50"
|
||||
enter-to="opacity-100 rotate-0 scale-100"
|
||||
leave="transform duration-200 transition ease-in-out"
|
||||
leave-from="opacity-100 rotate-0 scale-100 "
|
||||
leave-to="opacity-0 scale-95 "
|
||||
>
|
||||
<div class="h-full w-full rounded-md bg-white shadow-lg" />
|
||||
</TransitionRoot>
|
||||
</div>
|
||||
<button
|
||||
class="mt-8 flex transform items-center rounded-full bg-black bg-opacity-20 px-3 py-2 text-sm font-medium text-white transition hover:scale-105 hover:bg-opacity-30 focus:outline-none active:bg-opacity-40"
|
||||
@click="resetIsShowing"
|
||||
>
|
||||
<svg class="h-5 w-5 opacity-70" fill="none" viewBox="0 0 20 20">
|
||||
<path
|
||||
d="M14.9497 14.9498C12.2161 17.6835 7.78392 17.6835 5.05025 14.9498C2.31658 12.2162 2.31658 7.784 5.05025 5.05033C7.78392 2.31666 12.2161 2.31666 14.9497 5.05033C15.5333 5.63385 15.9922 6.29475 16.3266 7M16.9497 2L17 7H16.3266M12 7L16.3266 7"
|
||||
stroke="currentColor"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<span class="ml-3">Click to transition</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,90 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
const isDark = ref(false);
|
||||
|
||||
onMounted(() => {
|
||||
isDark.value = document.documentElement.classList.contains("dark");
|
||||
});
|
||||
|
||||
function toggle() {
|
||||
isDark.value = !isDark.value;
|
||||
document.documentElement.classList.toggle("dark", isDark.value);
|
||||
localStorage.setItem("theme", isDark.value ? "dark" : "light");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button
|
||||
class="theme-toggle"
|
||||
type="button"
|
||||
:aria-label="isDark ? 'Switch to light mode' : 'Switch to dark mode'"
|
||||
@click="toggle"
|
||||
>
|
||||
<!-- Moon: shown in light mode -->
|
||||
<svg
|
||||
v-if="!isDark"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
||||
</svg>
|
||||
<!-- Sun: shown in dark mode -->
|
||||
<svg
|
||||
v-else
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="17"
|
||||
height="17"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx="12" cy="12" r="5" />
|
||||
<line x1="12" y1="1" x2="12" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="23" />
|
||||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64" />
|
||||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78" />
|
||||
<line x1="1" y1="12" x2="3" y2="12" />
|
||||
<line x1="21" y1="12" x2="23" y2="12" />
|
||||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36" />
|
||||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22" />
|
||||
</svg>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.theme-toggle {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
color: var(--ink-2);
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 0.15s ease,
|
||||
color 0.15s ease;
|
||||
}
|
||||
|
||||
.theme-toggle:hover {
|
||||
background: var(--bg-raised);
|
||||
color: var(--ink);
|
||||
}
|
||||
</style>
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
declare namespace astroHTML.JSX {
|
||||
interface HTMLAttributes {
|
||||
"th:each"?: string;
|
||||
"th:href"?: string;
|
||||
"th:text"?: string;
|
||||
"th:if"?: string;
|
||||
"th:unless"?: string;
|
||||
"th:utext"?: string;
|
||||
"th:src"?: string;
|
||||
"th:with"?: string;
|
||||
"th:target"?: string;
|
||||
}
|
||||
}
|
||||
+44
-61
@@ -1,69 +1,52 @@
|
||||
---
|
||||
export interface Props {
|
||||
title: string;
|
||||
import "../styles/global.css";
|
||||
import Footer from "../components/Footer.astro";
|
||||
import Header from "../components/Header.astro";
|
||||
|
||||
interface Props {
|
||||
pageTitle?: string;
|
||||
contentClass?: string;
|
||||
}
|
||||
|
||||
const { title } = Astro.props as Props;
|
||||
const { pageTitle, contentClass } = Astro.props;
|
||||
|
||||
const hasHeading = Boolean(pageTitle);
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{title}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="flex justify-between">
|
||||
<nav class="flex gap-3">
|
||||
<a href="/" th:href="@{/}">Home</a>
|
||||
<a href="/post" th:href="@{/post}">Post</a>
|
||||
<a href="/links" th:href="@{/links}">Links</a>
|
||||
</nav>
|
||||
<div>
|
||||
<a href="/">默认</a>
|
||||
<a href="/?language=zh">中文</a>
|
||||
<a href="/?language=en">英文</a>
|
||||
</div>
|
||||
</div>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<script is:inline>
|
||||
(function () {
|
||||
var stored = localStorage.getItem("theme");
|
||||
var prefersDark = window.matchMedia(
|
||||
"(prefers-color-scheme: dark)",
|
||||
).matches;
|
||||
if (stored === "dark" || (!stored && prefersDark)) {
|
||||
document.documentElement.classList.add("dark");
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<slot name="head" />
|
||||
</head>
|
||||
<body>
|
||||
<Header />
|
||||
<main class="layout-main">
|
||||
<article class="page-shell">
|
||||
{
|
||||
hasHeading && (
|
||||
<header class="page-heading">
|
||||
{pageTitle && <h1>{pageTitle}</h1>}
|
||||
</header>
|
||||
)
|
||||
}
|
||||
<div class:list={["page-content", contentClass]}>
|
||||
<slot />
|
||||
</body>
|
||||
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
||||
<Footer />
|
||||
</body>
|
||||
</html>
|
||||
<style>
|
||||
:root {
|
||||
--font-size-base: clamp(1rem, 0.34vw + 0.91rem, 1.19rem);
|
||||
--font-size-lg: clamp(1.2rem, 0.7vw + 1.2rem, 1.5rem);
|
||||
--font-size-xl: clamp(2.44rem, 2.38vw + 1.85rem, 3.75rem);
|
||||
|
||||
--color-text: hsl(12, 5%, 4%);
|
||||
--color-bg: hsl(10, 21%, 95%);
|
||||
--color-border: hsl(17, 24%, 90%);
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: system-ui, sans-serif;
|
||||
font-size: var(--font-size-base);
|
||||
color: var(--color-text);
|
||||
background-color: var(--color-bg);
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
:global(h1) {
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
:global(h2) {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
:global(code) {
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout pageTitle="归档">
|
||||
<Fragment slot="head">
|
||||
<title th:text="|归档 - ${site.title}|"></title>
|
||||
</Fragment>
|
||||
|
||||
<section class="archive-list" aria-label="Archive list">
|
||||
<th:block th:each="archive : ${archives.items}">
|
||||
<th:block th:each="month : ${archive.months}">
|
||||
<div class="archive-group">
|
||||
<h2
|
||||
class="archive-group__year"
|
||||
th:text="|${archive.year} · ${month.month}|"
|
||||
>
|
||||
</h2>
|
||||
<ul class="archive-group__items">
|
||||
<li th:each="post : ${month.posts}" class="archive-entry">
|
||||
<span
|
||||
class="archive-entry__date"
|
||||
th:text="${#dates.format(post.spec.publishTime, 'MM/dd')}"
|
||||
></span>
|
||||
<div>
|
||||
<h3 class="archive-entry__title">
|
||||
<a
|
||||
th:href="@{${post.status.permalink}}"
|
||||
th:text="${post.spec.title}"></a>
|
||||
</h3>
|
||||
<p
|
||||
class="archive-entry__summary"
|
||||
th:text="${post.status.excerpt}"
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</th:block>
|
||||
|
||||
<div th:if="${archives.total == 0}" class="archive-empty">暂无文章。</div>
|
||||
</section>
|
||||
|
||||
<nav
|
||||
th:if="${archives.totalPages gt 1}"
|
||||
class="pagination"
|
||||
aria-label="Pagination"
|
||||
>
|
||||
<a
|
||||
th:if="${archives.hasPrevious()}"
|
||||
th:href="@{${archives.prevUrl}}"
|
||||
class="pagination__prev">← 较新</a
|
||||
>
|
||||
<span
|
||||
class="pagination__info"
|
||||
th:text="|${archives.page} / ${archives.totalPages}|"></span>
|
||||
<a
|
||||
th:if="${archives.hasNext()}"
|
||||
th:href="@{${archives.nextUrl}}"
|
||||
class="pagination__next">较旧 →</a
|
||||
>
|
||||
</nav>
|
||||
</Layout>
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout pageTitle="分类">
|
||||
<Fragment slot="head">
|
||||
<title th:text="|分类 - ${site.title}|"></title>
|
||||
</Fragment>
|
||||
|
||||
<nav class="taxonomy-list" aria-label="Category list">
|
||||
<div
|
||||
th:each="category : ${categories}"
|
||||
class="taxonomy-list__item"
|
||||
>
|
||||
<a
|
||||
class="taxonomy-list__link"
|
||||
th:href="@{${category.status.permalink}}"
|
||||
th:text="${category.spec.displayName}"
|
||||
>
|
||||
</a>
|
||||
<span class="taxonomy-list__count" th:text="|${category.postCount} 篇|">
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
</Layout>
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<Fragment slot="head">
|
||||
<title th:text="|${category.spec.displayName} - ${site.title}|"></title>
|
||||
</Fragment>
|
||||
|
||||
<div class="page-heading">
|
||||
<h1 th:text="${category.spec.displayName}"></h1>
|
||||
<p class="page-meta">
|
||||
<span th:text="|${posts.total} 篇文章|"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul class="post-feed__list" role="list">
|
||||
<li th:each="post : ${posts.items}" class="post-feed__item">
|
||||
<a class="post-feed__link" th:href="@{${post.status.permalink}}">
|
||||
<div class="post-feed__body">
|
||||
<p
|
||||
class="post-feed__date"
|
||||
th:text="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd')}"
|
||||
>
|
||||
</p>
|
||||
<h2 class="post-feed__title" th:text="${post.spec.title}"></h2>
|
||||
<p class="post-feed__excerpt" th:text="${post.status.excerpt}"></p>
|
||||
</div>
|
||||
<img
|
||||
th:unless="${#strings.isEmpty(post.spec.cover)}"
|
||||
class="post-feed__image"
|
||||
th:src="${post.spec.cover}"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div th:if="${posts.total == 0}" class="feed-empty">暂无文章。</div>
|
||||
|
||||
<nav
|
||||
th:if="${posts.totalPages gt 1}"
|
||||
class="pagination"
|
||||
aria-label="Pagination"
|
||||
>
|
||||
<a
|
||||
th:if="${posts.hasPrevious()}"
|
||||
th:href="@{${posts.prevUrl}}"
|
||||
class="pagination__prev">← 较新</a
|
||||
>
|
||||
<span
|
||||
class="pagination__info"
|
||||
th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a
|
||||
th:if="${posts.hasNext()}"
|
||||
th:href="@{${posts.nextUrl}}"
|
||||
class="pagination__next">较旧 →</a
|
||||
>
|
||||
</nav>
|
||||
</Layout>
|
||||
+42
-88
@@ -1,95 +1,49 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import Card from "../components/Card.astro";
|
||||
import PostCard from "../components/PostCard.vue"
|
||||
---
|
||||
|
||||
<Layout title="Welcome to Astro.">
|
||||
<main>
|
||||
<h1>Welcome to <span class="text-gradient">Astro</span></h1>
|
||||
<p class="instructions">
|
||||
Check out the <code>src/pages</code> directory to get started.<br />
|
||||
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
|
||||
<Layout>
|
||||
<Fragment slot="head">
|
||||
<title th:text="${site.title}"></title>
|
||||
</Fragment>
|
||||
<ul class="post-feed__list" role="list">
|
||||
<li th:each="post : ${posts.items}" class="post-feed__item">
|
||||
<a class="post-feed__link" th:href="@{${post.status.permalink}}">
|
||||
<div class="post-feed__body">
|
||||
<p
|
||||
class="post-feed__date"
|
||||
th:text="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd')}"
|
||||
>
|
||||
</p>
|
||||
<ul role="list" class="link-card-grid">
|
||||
<Card href="https://docs.astro.build/" title="Documentation"
|
||||
body="Learn how Astro works and explore the official API docs." />
|
||||
<Card href="https://astro.build/integrations/" title="Integrations"
|
||||
body="Supercharge your project with new frameworks and libraries." />
|
||||
<Card href="https://astro.build/themes/" title="Themes"
|
||||
body="Explore a galaxy of community-built starter themes." />
|
||||
<Card href="https://astro.build/chat/" title="Chat" body="Come say hi to our amazing Discord community. ❤️" />
|
||||
<h2 class="post-feed__title" th:text="${post.spec.title}"></h2>
|
||||
<p class="post-feed__excerpt" th:text="${post.status.excerpt}"></p>
|
||||
</div>
|
||||
<img
|
||||
th:unless="${#strings.isEmpty(post.spec.cover)}"
|
||||
class="post-feed__image"
|
||||
th:src="${post.spec.cover}"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-decimal list-inside">
|
||||
<li th:text="#{foo}"></li>
|
||||
<li th:text="#{bar}"></li>
|
||||
<li th:text="#{hello('Ryan Wang')}"></li>
|
||||
</ul>
|
||||
<PostCard client:visible />
|
||||
<p th:each="post : ${postFinder.list(1, 10)}">
|
||||
<span th:text="${post.name}">Onions</span>
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<nav
|
||||
th:if="${posts.totalPages gt 1}"
|
||||
class="pagination"
|
||||
aria-label="Pagination"
|
||||
>
|
||||
<a
|
||||
th:if="${posts.hasPrevious()}"
|
||||
th:href="@{${posts.prevUrl}}"
|
||||
class="pagination__prev">← 较新</a
|
||||
>
|
||||
<span
|
||||
class="pagination__info"
|
||||
th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a
|
||||
th:if="${posts.hasNext()}"
|
||||
th:href="@{${posts.nextUrl}}"
|
||||
class="pagination__next">较旧 →</a
|
||||
>
|
||||
</nav>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--astro-gradient: linear-gradient(0deg, #4f39fa, #da62c4);
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: auto;
|
||||
padding: 1em;
|
||||
max-width: 70ch;
|
||||
}
|
||||
|
||||
.text-gradient {
|
||||
font-weight: 900;
|
||||
background-image: var(--astro-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-size: 100% 200%;
|
||||
background-position-y: 100%;
|
||||
border-radius: 0.4rem;
|
||||
animation: pulse 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
background-position-y: 0%;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position-y: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.instructions {
|
||||
line-height: 1.6;
|
||||
margin: 1rem 0;
|
||||
background: #4f39fa;
|
||||
padding: 1rem;
|
||||
border-radius: 0.4rem;
|
||||
color: var(--color-bg);
|
||||
}
|
||||
|
||||
.instructions code {
|
||||
font-size: 0.875em;
|
||||
border: 0.1em solid var(--color-border);
|
||||
border-radius: 4px;
|
||||
padding: 0.15em 0.25em;
|
||||
}
|
||||
|
||||
.link-card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
|
||||
gap: 1rem;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
|
||||
const links = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Ryan Wang's Blog",
|
||||
"url": "https://ryanc.cc/",
|
||||
"logo": "https://ryanc.cc/avatar",
|
||||
"description": "Hello Halo",
|
||||
"team": "",
|
||||
"priority": 0
|
||||
},
|
||||
{
|
||||
"id": 40,
|
||||
"name": "guqing's blog",
|
||||
"url": "https://www.guqing.xyz/",
|
||||
"logo": "https://www.guqing.xyz/avatar",
|
||||
"description": "毕生所求无它,爱与自由而已",
|
||||
"team": "",
|
||||
"priority": 0
|
||||
},
|
||||
{
|
||||
"id": 104,
|
||||
"name": "JohnNiang's Blog",
|
||||
"url": "https://johnniang.me",
|
||||
"logo": "https://johnniang.me/avatar",
|
||||
"description": "Core contributor of Halo organization.",
|
||||
"team": "",
|
||||
"priority": 0
|
||||
},
|
||||
{
|
||||
"id": 152,
|
||||
"name": "烟霞志",
|
||||
"url": "https://simplestark.com",
|
||||
"logo": "https://simplestark.com/avatar",
|
||||
"description": "空有烟霞之志,叹无水云之身。",
|
||||
"team": "",
|
||||
"priority": 0
|
||||
},
|
||||
]
|
||||
---
|
||||
|
||||
<Layout title="Links">
|
||||
<main>
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2">
|
||||
|
||||
{links.map((data) => <a href={data.url} th:href="#{data.url}" target="_blank">
|
||||
<div
|
||||
class="relative rounded border border-gray-300 bg-white px-5 py-4 shadow-sm hover:shadow flex items-center space-x-3 hover:border-gray-400">
|
||||
<div class="flex-shrink-0">
|
||||
<img class="h-12 w-12 rounded-full" th:src="#{data.logo}" th:alt="#{data.name}" src={data.logo}
|
||||
alt={data.name}>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div>
|
||||
<p class="text-sm font-medium text-gray-900 truncate" th:text="#{data.name}">{data.name}</p>
|
||||
<p class="text-sm text-gray-500 truncate" th:text="#{data.description}">{data.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>)}
|
||||
</div>
|
||||
</main>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
main {
|
||||
margin: auto;
|
||||
padding: 1em;
|
||||
max-width: 70ch;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout contentClass="prose">
|
||||
<Fragment slot="head">
|
||||
<title th:text="|${singlePage.spec.title} - ${site.title}|"></title>
|
||||
</Fragment>
|
||||
|
||||
<div class="page-heading">
|
||||
<h1 th:text="${singlePage.spec.title}"></h1>
|
||||
<p class="page-meta">
|
||||
<span
|
||||
th:text="${#dates.format(singlePage.spec.publishTime, 'yyyy-MM-dd')}"
|
||||
></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div th:utext="${singlePage.content.content}"></div>
|
||||
|
||||
<halo:comment
|
||||
group="content.halo.run"
|
||||
kind="SinglePage"
|
||||
th:attr="name=${singlePage.metadata.name}"></halo:comment>
|
||||
</Layout>
|
||||
+21
-9
@@ -1,14 +1,26 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
import PostCard from "../components/PostCard.vue"
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const now = dayjs()
|
||||
---
|
||||
|
||||
<Layout title="Welcome to Post Page.">
|
||||
<time>{now}</time>
|
||||
<main>
|
||||
<PostCard client:visible />
|
||||
</main>
|
||||
<Layout contentClass="prose">
|
||||
<Fragment slot="head">
|
||||
<title th:text="|${post.spec.title} - ${site.title}|"></title>
|
||||
</Fragment>
|
||||
|
||||
<div class="page-heading">
|
||||
<h1 th:text="${post.spec.title}"></h1>
|
||||
<p class="page-meta">
|
||||
<span th:text="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd')}"
|
||||
></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<th:block th:utext="${post.content.content}"></th:block>
|
||||
|
||||
<halo:comment
|
||||
group="content.halo.run"
|
||||
kind="Post"
|
||||
th:attr="name=${post.metadata.name}"
|
||||
>
|
||||
</halo:comment>
|
||||
</Layout>
|
||||
@@ -0,0 +1,59 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout>
|
||||
<Fragment slot="head">
|
||||
<title th:text="|${tag.spec.displayName} - ${site.title}|"></title>
|
||||
</Fragment>
|
||||
|
||||
<div class="page-heading">
|
||||
<h1 th:text="${tag.spec.displayName}"></h1>
|
||||
<p class="page-meta">
|
||||
<span th:text="|${posts.total} 篇文章|"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ul class="post-feed__list" role="list">
|
||||
<li th:each="post : ${posts.items}" class="post-feed__item">
|
||||
<a class="post-feed__link" th:href="@{${post.status.permalink}}">
|
||||
<div class="post-feed__body">
|
||||
<p
|
||||
class="post-feed__date"
|
||||
th:text="${#dates.format(post.spec.publishTime, 'yyyy-MM-dd')}"
|
||||
>
|
||||
</p>
|
||||
<h2 class="post-feed__title" th:text="${post.spec.title}"></h2>
|
||||
<p class="post-feed__excerpt" th:text="${post.status.excerpt}"></p>
|
||||
</div>
|
||||
<img
|
||||
th:unless="${#strings.isEmpty(post.spec.cover)}"
|
||||
class="post-feed__image"
|
||||
th:src="${post.spec.cover}"
|
||||
/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div th:if="${posts.total == 0}" class="feed-empty">暂无文章。</div>
|
||||
|
||||
<nav
|
||||
th:if="${posts.totalPages gt 1}"
|
||||
class="pagination"
|
||||
aria-label="Pagination"
|
||||
>
|
||||
<a
|
||||
th:if="${posts.hasPrevious()}"
|
||||
th:href="@{${posts.prevUrl}}"
|
||||
class="pagination__prev">← 较新</a
|
||||
>
|
||||
<span
|
||||
class="pagination__info"
|
||||
th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a
|
||||
th:if="${posts.hasNext()}"
|
||||
th:href="@{${posts.nextUrl}}"
|
||||
class="pagination__next">较旧 →</a
|
||||
>
|
||||
</nav>
|
||||
</Layout>
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
import Layout from "../layouts/Layout.astro";
|
||||
---
|
||||
|
||||
<Layout pageTitle="标签">
|
||||
<Fragment slot="head">
|
||||
<title th:text="|标签 - ${site.title}|"></title>
|
||||
</Fragment>
|
||||
|
||||
<div class="tag-cloud" role="list" aria-label="Tag list">
|
||||
<a
|
||||
th:each="tag : ${tags}"
|
||||
class="tag-pill"
|
||||
role="listitem"
|
||||
th:href="@{${tag.status.permalink}}"
|
||||
>
|
||||
<span th:text="${tag.spec.displayName}"></span>
|
||||
<span class="tag-pill__count" th:text="${tag.postCount}"></span>
|
||||
</a>
|
||||
</div>
|
||||
</Layout>
|
||||
@@ -0,0 +1,465 @@
|
||||
:root {
|
||||
--bg: #faf8f5;
|
||||
--bg-raised: #f2ede8;
|
||||
--ink: #1a1410;
|
||||
--ink-2: #7a7068;
|
||||
--ink-3: #b5aca2;
|
||||
--rule: #ddd8d0;
|
||||
--accent: #c2410c;
|
||||
--accent-bg: #fff4ed;
|
||||
--black: 26, 20, 16;
|
||||
--gray: 122, 112, 104;
|
||||
--gray-light: 221, 216, 208;
|
||||
--gray-dark: 44, 36, 30;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
--bg: #181613;
|
||||
--bg-raised: #242018;
|
||||
--ink: #ede9e3;
|
||||
--ink-2: #9a9088;
|
||||
--ink-3: #5e5650;
|
||||
--rule: #2e2a24;
|
||||
--accent: #f47040;
|
||||
--accent-bg: #2a1a0c;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: ui-sans-serif, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: var(--bg);
|
||||
color: var(--ink);
|
||||
font-size: 17px;
|
||||
line-height: 1.75;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 820px;
|
||||
max-width: calc(100% - 2.5rem);
|
||||
margin: 0 auto;
|
||||
padding: 3rem 0 5rem;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-family: ui-sans-serif, sans-serif;
|
||||
margin: 0 0 0.5em;
|
||||
color: var(--ink);
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
strong,
|
||||
b {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
text-decoration-thickness: 1px;
|
||||
text-underline-offset: 0.2em;
|
||||
}
|
||||
a:hover {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
input {
|
||||
font-size: 16px;
|
||||
}
|
||||
table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
img,
|
||||
video,
|
||||
iframe,
|
||||
embed,
|
||||
object {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.88em;
|
||||
padding: 0.15em 0.4em;
|
||||
background: var(--bg-raised);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--rule);
|
||||
}
|
||||
pre {
|
||||
padding: 1.4em 1.6em;
|
||||
border-radius: 10px;
|
||||
overflow-x: auto;
|
||||
background: var(--ink);
|
||||
color: #f8f8f2;
|
||||
}
|
||||
pre > code {
|
||||
all: unset;
|
||||
font-size: 0.9em;
|
||||
color: inherit;
|
||||
}
|
||||
blockquote {
|
||||
margin: 1.5em 0;
|
||||
padding: 0.75em 0 0.75em 1.5em;
|
||||
border-left: 3px solid var(--accent);
|
||||
color: var(--ink-2);
|
||||
font-size: 1.1em;
|
||||
}
|
||||
hr {
|
||||
margin: 2.5rem 0;
|
||||
border: none;
|
||||
border-top: 1px solid var(--rule);
|
||||
}
|
||||
|
||||
/* ─── Page shell ─────────────────────────────────── */
|
||||
|
||||
.page-shell {
|
||||
display: grid;
|
||||
gap: 2.5rem;
|
||||
}
|
||||
|
||||
.page-heading {
|
||||
padding-bottom: 1.5rem;
|
||||
border-bottom: 1px solid var(--rule);
|
||||
}
|
||||
|
||||
.page-heading h1 {
|
||||
margin-bottom: 0.25em;
|
||||
}
|
||||
|
||||
.page-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
margin-bottom: 0.6rem;
|
||||
color: var(--ink-3);
|
||||
font-size: 0.78rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.page-content {
|
||||
display: grid;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
/* ─── Post feed ──────────────────────────────────── */
|
||||
|
||||
.post-feed__list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.post-feed__item + .post-feed__item {
|
||||
border-top: 1px solid var(--rule);
|
||||
}
|
||||
|
||||
.post-feed__link {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) 200px;
|
||||
gap: 2rem;
|
||||
align-items: start;
|
||||
padding: 2rem 0;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.post-feed__item:first-child .post-feed__link {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.post-feed__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.55rem;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.post-feed__date {
|
||||
margin: 0;
|
||||
color: var(--ink-3);
|
||||
font-size: 0.8rem;
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.post-feed__title {
|
||||
margin: 0;
|
||||
font-size: 1.35rem;
|
||||
line-height: 1.3;
|
||||
color: var(--ink);
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.post-feed__excerpt {
|
||||
margin: 0;
|
||||
color: var(--ink-2);
|
||||
font-size: 0.96rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.post-feed__image {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 4 / 3;
|
||||
object-fit: cover;
|
||||
border-radius: 10px;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.post-feed__link:hover .post-feed__title {
|
||||
color: var(--accent);
|
||||
}
|
||||
.post-feed__link:hover .post-feed__image {
|
||||
opacity: 0.88;
|
||||
}
|
||||
|
||||
/* ─── Taxonomy list (categories) ─────────────────── */
|
||||
|
||||
.taxonomy-list__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 0.55rem 0;
|
||||
}
|
||||
|
||||
.taxonomy-list__link {
|
||||
color: var(--ink);
|
||||
font-size: 1.05rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.taxonomy-list__link:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.taxonomy-list__count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 0.2em 0.7em;
|
||||
border-radius: 999px;
|
||||
background: var(--bg-raised);
|
||||
border: 1px solid var(--rule);
|
||||
color: var(--ink-2);
|
||||
font-size: 0.82rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ─── Tag cloud (badges) ─────────────────────────── */
|
||||
|
||||
.tag-cloud {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.tag-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.35em 0.85em;
|
||||
border-radius: 6px;
|
||||
background: var(--bg-raised);
|
||||
border: 1px solid var(--rule);
|
||||
color: var(--ink-2);
|
||||
font-size: 0.88rem;
|
||||
text-decoration: none;
|
||||
transition:
|
||||
background 0.15s ease,
|
||||
color 0.15s ease,
|
||||
border-color 0.15s ease;
|
||||
}
|
||||
|
||||
.tag-pill:hover {
|
||||
background: var(--accent-bg);
|
||||
color: var(--accent);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.tag-pill__count {
|
||||
font-size: 0.8em;
|
||||
color: var(--ink-3);
|
||||
}
|
||||
|
||||
/* ─── Archive ────────────────────────────────────── */
|
||||
|
||||
.archive-list {
|
||||
display: grid;
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
.archive-group {
|
||||
display: grid;
|
||||
grid-template-columns: 80px minmax(0, 1fr);
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.archive-group__year {
|
||||
margin: 0;
|
||||
font-size: 1.05rem;
|
||||
font-weight: 700;
|
||||
color: var(--ink-3);
|
||||
letter-spacing: -0.01em;
|
||||
padding-top: 0.2rem;
|
||||
}
|
||||
|
||||
.archive-group__items {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.archive-entry {
|
||||
display: grid;
|
||||
grid-template-columns: 80px minmax(0, 1fr);
|
||||
gap: 1rem;
|
||||
padding: 0.5rem 0;
|
||||
}
|
||||
|
||||
.archive-entry__date {
|
||||
color: var(--ink-3);
|
||||
font-size: 0.85rem;
|
||||
padding-top: 0.15rem;
|
||||
}
|
||||
|
||||
.archive-entry__title {
|
||||
margin: 0 0 0.2rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.archive-entry__summary {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
color: var(--ink-2);
|
||||
}
|
||||
|
||||
/* ─── Responsive ─────────────────────────────────── */
|
||||
|
||||
@media (max-width: 680px) {
|
||||
main {
|
||||
max-width: calc(100% - 2rem);
|
||||
padding: 2rem 0 4rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.9rem;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.post-feed__link {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
padding: 1.5rem 0;
|
||||
}
|
||||
|
||||
.post-feed__image {
|
||||
aspect-ratio: 16 / 9;
|
||||
order: -1;
|
||||
}
|
||||
|
||||
.taxonomy-list__item,
|
||||
.archive-group,
|
||||
.archive-entry {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.archive-group {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.taxonomy-list__item {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* ─── Pagination ─────────────────────────────────── */
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid var(--rule);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.pagination__prev,
|
||||
.pagination__next {
|
||||
color: var(--ink-2);
|
||||
text-decoration: none;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.pagination__prev:hover,
|
||||
.pagination__next:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.pagination__info {
|
||||
color: var(--ink-3);
|
||||
}
|
||||
|
||||
.archive-empty,
|
||||
.feed-empty {
|
||||
color: var(--ink-3);
|
||||
font-size: 0.95rem;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>About</title>
|
||||
</head>
|
||||
<body>
|
||||
<p
|
||||
th:text="没有经过 Astro 渲染,原生使用 Thymeleaf 语法。在构建时,将被 Copy 到 templates 目录。(Thymeleaf)"
|
||||
>
|
||||
没有经过 Astro 渲染,原生使用 Thymeleaf 语法。在构建时,将被 Copy 到
|
||||
templates 目录。(Native)
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
import{c as r,a as l,b as s,d,o as c,r as u,e as i}from"./runtime-core.esm-bundler.25dbJ6OW.js";const x=(a,t)=>{const e=a.__vccOpts||a;for(const[n,o]of t)e[n]=o;return e},v=d({__name:"ThemeSwitcher",setup(a,{expose:t}){t();const e=u(!1);c(()=>{e.value=document.documentElement.classList.contains("dark")});function n(){e.value=!e.value,document.documentElement.classList.toggle("dark",e.value),localStorage.setItem("theme",e.value?"dark":"light")}const o={isDark:e,toggle:n};return Object.defineProperty(o,"__isScriptSetup",{enumerable:!1,value:!0}),o}}),_=["aria-label"],h={key:0,xmlns:"http://www.w3.org/2000/svg",width:"17",height:"17",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round","aria-hidden":"true"},m={key:1,xmlns:"http://www.w3.org/2000/svg",width:"17",height:"17",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor","stroke-width":"2","stroke-linecap":"round","stroke-linejoin":"round","aria-hidden":"true"};function y(a,t,e,n,o,k){return i(),r("button",{class:"theme-toggle",type:"button","aria-label":n.isDark?"Switch to light mode":"Switch to dark mode",onClick:n.toggle},[n.isDark?(i(),r("svg",m,[...t[1]||(t[1]=[s('<circle cx="12" cy="12" r="5" data-v-976d6eb8></circle><line x1="12" y1="1" x2="12" y2="3" data-v-976d6eb8></line><line x1="12" y1="21" x2="12" y2="23" data-v-976d6eb8></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64" data-v-976d6eb8></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78" data-v-976d6eb8></line><line x1="1" y1="12" x2="3" y2="12" data-v-976d6eb8></line><line x1="21" y1="12" x2="23" y2="12" data-v-976d6eb8></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36" data-v-976d6eb8></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22" data-v-976d6eb8></line>',9)])])):(i(),r("svg",h,[...t[0]||(t[0]=[l("path",{d:"M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"},null,-1)])]))],8,_)}const g=x(v,[["render",y],["__scopeId","data-v-976d6eb8"]]);export{g as default};
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
||||
:root{--astro-gradient: linear-gradient(0deg, #4f39fa, #da62c4)}h1:where(.astro-AQ2P6PWU){margin:2rem 0}main:where(.astro-AQ2P6PWU){margin:auto;padding:1em;max-width:70ch}.text-gradient:where(.astro-AQ2P6PWU){font-weight:900;background-image:var(--astro-gradient);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-size:100% 200%;background-position-y:100%;border-radius:.4rem;-webkit-animation:pulse 4s ease-in-out infinite;animation:pulse 4s ease-in-out infinite}@-webkit-keyframes pulse{0%,to{background-position-y:0%}50%{background-position-y:80%}}@keyframes pulse{0%,to{background-position-y:0%}50%{background-position-y:80%}}.instructions:where(.astro-AQ2P6PWU){line-height:1.6;margin:1rem 0;background:#4f39fa;padding:1rem;border-radius:.4rem;color:var(--color-bg)}.instructions:where(.astro-AQ2P6PWU) code:where(.astro-AQ2P6PWU){font-size:.875em;border:.1em solid var(--color-border);border-radius:4px;padding:.15em .25em}.link-card-grid:where(.astro-AQ2P6PWU){display:grid;grid-template-columns:repeat(auto-fit,minmax(24ch,1fr));gap:1rem;padding:0}:root{--link-gradient: linear-gradient(45deg, #4f39fa, #da62c4 30%, var(--color-border) 60%)}.link-card:where(.astro-DYDDWWYO){list-style:none;display:flex;padding:.15rem;background-image:var(--link-gradient);background-size:400%;border-radius:.5rem;background-position:100%;transition:background-position .6s cubic-bezier(.22,1,.36,1)}.link-card:where(.astro-DYDDWWYO)>a:where(.astro-DYDDWWYO){width:100%;text-decoration:none;line-height:1.4;padding:1em 1.3em;border-radius:.35rem;color:var(--text-color);background-color:#fff;opacity:.8}h2:where(.astro-DYDDWWYO){margin:0;transition:color .6s cubic-bezier(.22,1,.36,1)}p:where(.astro-DYDDWWYO){margin-top:.75rem;margin-bottom:0}h2:where(.astro-DYDDWWYO) span:where(.astro-DYDDWWYO){display:inline-block;transition:transform .3s cubic-bezier(.22,1,.36,1)}.link-card:where(.astro-DYDDWWYO):is(:hover,:focus-within){background-position:0}.link-card:where(.astro-DYDDWWYO):is(:hover,:focus-within) h2:where(.astro-DYDDWWYO){color:#4f39fa}.link-card:where(.astro-DYDDWWYO):is(:hover,:focus-within) h2:where(.astro-DYDDWWYO) span:where(.astro-DYDDWWYO){transform:translate(2px)}
|
||||
@@ -1 +0,0 @@
|
||||
main:where(.astro-SLXJX4NQ){margin:auto;padding:1em;max-width:70ch}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
+13
-86
File diff suppressed because one or more lines are too long
@@ -1,78 +0,0 @@
|
||||
<!DOCTYPE html><html lang="en" class="astro-TFKA5VKL"><head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>Links</title>
|
||||
<link rel="stylesheet" href="/assets/index-links-post.be069062.css" th:href="@{/assets/index-links-post.be069062.css}">
|
||||
<link rel="stylesheet" href="/assets/links.5e24488a.css" th:href="@{/assets/links.5e24488a.css}"></head>
|
||||
|
||||
<body class="astro-TFKA5VKL">
|
||||
<div class="flex justify-between astro-TFKA5VKL">
|
||||
<nav class="flex gap-3 astro-TFKA5VKL">
|
||||
<a href="/" th:href="@{/}" class="astro-TFKA5VKL">Home</a>
|
||||
<a href="/post" th:href="@{/post}" class="astro-TFKA5VKL">Post</a>
|
||||
<a href="/links" th:href="@{/links}" class="astro-TFKA5VKL">Links</a>
|
||||
</nav>
|
||||
<div class="astro-TFKA5VKL">
|
||||
<a href="/" class="astro-TFKA5VKL">默认</a>
|
||||
<a href="/?language=zh" class="astro-TFKA5VKL">中文</a>
|
||||
<a href="/?language=en" class="astro-TFKA5VKL">英文</a>
|
||||
</div>
|
||||
</div>
|
||||
<main class="astro-SLXJX4NQ">
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 astro-SLXJX4NQ">
|
||||
|
||||
<a href="https://ryanc.cc/" th:href="#{data.url}" target="_blank" class="astro-SLXJX4NQ">
|
||||
<div class="relative rounded border border-gray-300 bg-white px-5 py-4 shadow-sm hover:shadow flex items-center space-x-3 hover:border-gray-400 astro-SLXJX4NQ">
|
||||
<div class="flex-shrink-0 astro-SLXJX4NQ">
|
||||
<img class="h-12 w-12 rounded-full astro-SLXJX4NQ" th:src="#{data.logo}" th:alt="#{data.name}" src="https://ryanc.cc/avatar" alt="Ryan Wang's Blog">
|
||||
</div>
|
||||
<div class="flex-1 min-w-0 astro-SLXJX4NQ">
|
||||
<div class="astro-SLXJX4NQ">
|
||||
<p class="text-sm font-medium text-gray-900 truncate astro-SLXJX4NQ" th:text="#{data.name}">Ryan Wang's Blog</p>
|
||||
<p class="text-sm text-gray-500 truncate astro-SLXJX4NQ" th:text="#{data.description}">Hello Halo</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a><a href="https://www.guqing.xyz/" th:href="#{data.url}" target="_blank" class="astro-SLXJX4NQ">
|
||||
<div class="relative rounded border border-gray-300 bg-white px-5 py-4 shadow-sm hover:shadow flex items-center space-x-3 hover:border-gray-400 astro-SLXJX4NQ">
|
||||
<div class="flex-shrink-0 astro-SLXJX4NQ">
|
||||
<img class="h-12 w-12 rounded-full astro-SLXJX4NQ" th:src="#{data.logo}" th:alt="#{data.name}" src="https://www.guqing.xyz/avatar" alt="guqing's blog">
|
||||
</div>
|
||||
<div class="flex-1 min-w-0 astro-SLXJX4NQ">
|
||||
<div class="astro-SLXJX4NQ">
|
||||
<p class="text-sm font-medium text-gray-900 truncate astro-SLXJX4NQ" th:text="#{data.name}">guqing's blog</p>
|
||||
<p class="text-sm text-gray-500 truncate astro-SLXJX4NQ" th:text="#{data.description}">毕生所求无它,爱与自由而已</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a><a href="https://johnniang.me" th:href="#{data.url}" target="_blank" class="astro-SLXJX4NQ">
|
||||
<div class="relative rounded border border-gray-300 bg-white px-5 py-4 shadow-sm hover:shadow flex items-center space-x-3 hover:border-gray-400 astro-SLXJX4NQ">
|
||||
<div class="flex-shrink-0 astro-SLXJX4NQ">
|
||||
<img class="h-12 w-12 rounded-full astro-SLXJX4NQ" th:src="#{data.logo}" th:alt="#{data.name}" src="https://johnniang.me/avatar" alt="JohnNiang's Blog">
|
||||
</div>
|
||||
<div class="flex-1 min-w-0 astro-SLXJX4NQ">
|
||||
<div class="astro-SLXJX4NQ">
|
||||
<p class="text-sm font-medium text-gray-900 truncate astro-SLXJX4NQ" th:text="#{data.name}">JohnNiang's Blog</p>
|
||||
<p class="text-sm text-gray-500 truncate astro-SLXJX4NQ" th:text="#{data.description}">Core contributor of Halo organization.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a><a href="https://simplestark.com" th:href="#{data.url}" target="_blank" class="astro-SLXJX4NQ">
|
||||
<div class="relative rounded border border-gray-300 bg-white px-5 py-4 shadow-sm hover:shadow flex items-center space-x-3 hover:border-gray-400 astro-SLXJX4NQ">
|
||||
<div class="flex-shrink-0 astro-SLXJX4NQ">
|
||||
<img class="h-12 w-12 rounded-full astro-SLXJX4NQ" th:src="#{data.logo}" th:alt="#{data.name}" src="https://simplestark.com/avatar" alt="烟霞志">
|
||||
</div>
|
||||
<div class="flex-1 min-w-0 astro-SLXJX4NQ">
|
||||
<div class="astro-SLXJX4NQ">
|
||||
<p class="text-sm font-medium text-gray-900 truncate astro-SLXJX4NQ" th:text="#{data.name}">烟霞志</p>
|
||||
<p class="text-sm text-gray-500 truncate astro-SLXJX4NQ" th:text="#{data.description}">空有烟霞之志,叹无水云之身。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
|
||||
</body></html>
|
||||
File diff suppressed because one or more lines are too long
+13
-24
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+13
-12
@@ -1,17 +1,18 @@
|
||||
apiVersion: theme.halo.run/v1alpha1
|
||||
kind: Theme
|
||||
metadata:
|
||||
name: astro-starter
|
||||
spec:
|
||||
displayName: Astro Starter
|
||||
author:
|
||||
name: Halo OSS Organization
|
||||
website: "https://halo.run"
|
||||
description: Halo 2.0 Theme base on Astro and Thymeleaf
|
||||
logo: "https://halo.run/logo"
|
||||
website: "https://halo.run"
|
||||
repo: "https://github.com/halo-sigs/theme-astro-starter"
|
||||
settingName: "theme-astro-starter-setting"
|
||||
configMapName: "theme-astro-starter-configMap"
|
||||
version: "1.0"
|
||||
require: "*"
|
||||
metadata:
|
||||
name: theme-astro-starter
|
||||
name: Your Name
|
||||
website: https://github.com/YourName
|
||||
description: A starter theme for Halo.
|
||||
logo: https://www.halo.run/logo
|
||||
homepage: https://github.com/halo-sigs/theme-astro-starter
|
||||
issues: https://github.com/halo-sigs/theme-astro-starter/issues
|
||||
version: "1.0.0"
|
||||
requires: ">=2.0.0"
|
||||
license:
|
||||
- name: "GPL-3.0"
|
||||
url: "https://www.gnu.org/licenses/gpl-3.0.en.html"
|
||||
|
||||
+5
-16
@@ -1,20 +1,9 @@
|
||||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"],
|
||||
"compilerOptions": {
|
||||
// Enable top-level await, and other modern ESM features.
|
||||
"lib": [
|
||||
"dom"
|
||||
],
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
// Enable node-style module resolution, for things like npm package imports.
|
||||
"moduleResolution": "node",
|
||||
// Enable JSON imports.
|
||||
"resolveJsonModule": true,
|
||||
// Enable stricter transpilation for better output.
|
||||
"isolatedModules": true,
|
||||
// Add type definitions for our Astro runtime.
|
||||
"types": [
|
||||
"astro/client"
|
||||
]
|
||||
"strictNullChecks": true,
|
||||
"jsx": "preserve"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user