feat(blog): 首次提交
这个提交包含在:
+29
@@ -0,0 +1,29 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
.gradle
|
||||||
|
build
|
||||||
|
|
||||||
|
dev/data
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
services:
|
||||||
|
halo:
|
||||||
|
image: registry.fit2cloud.com/halo/halo:2.18
|
||||||
|
volumes:
|
||||||
|
- ./data:/root/.halo2
|
||||||
|
- ../theme-terminal:/root/.halo2/themes/theme-terminal
|
||||||
|
ports:
|
||||||
|
- "8090:8090"
|
||||||
|
environment:
|
||||||
|
SPRING_THYMELEAF_CACHE: false
|
||||||
|
command:
|
||||||
|
- --halo.external-url=http://localhost:8090/
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# 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 = false
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parser: "@typescript-eslint/parser",
|
||||||
|
plugins: ["@typescript-eslint", "prettier"],
|
||||||
|
extends: [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/eslint-recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"prettier",
|
||||||
|
],
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Halo Terminal
|
||||||
|
|
||||||
|
一款 Terminal 风格的 Halo 主题。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 特性
|
||||||
|
- 支持明暗模式切换;
|
||||||
|
- 首页公告设置;
|
||||||
|
- 备案信息设置;
|
||||||
|
- 支持 Halo 内部的大部分页面,包括文章、页面、分类、标签、归档等。
|
||||||
|
- 支持 Halo 应用市场的部分内容管理插件,包括友情链接、瞬间。
|
||||||
|
|
||||||
|
### 致谢
|
||||||
|
|
||||||
|
- [Hugo Terminal](https://github.com/panr/hugo-theme-terminal):Terminal 风格的 Hugo 主题,提供了最初的主题思路。
|
||||||
|
- [Zola Terminimal](https://github.com/pawroman/zola-theme-terminimal):Hugo Terminal 主题的 Zola 移植版本,使用了该主题修改后的部分样式。
|
||||||
|
- [iTerm2-Color-Schemes](https://github.com/mbadolato/iTerm2-Color-Schemes/):iTerm 配色方案仓库,使用了该仓库中提供的配色方案。
|
||||||
第三方依赖
+10
@@ -0,0 +1,10 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
|
import type { Alpine } from "alpinejs";
|
||||||
|
|
||||||
|
export {};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
Alpine: Alpine;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"name": "theme-terminal",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A terminal like theme for Halo.",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite build --watch",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"lint": "eslint ./src --ext .js,.cjs,.mjs,.ts,.cts,.mts --ignore-path .gitignore",
|
||||||
|
"prettier": "prettier --write './src/**/*.{js,ts,css,json,ml,yaml,html}' './templates/**/*.html'"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"halo",
|
||||||
|
"halo-theme"
|
||||||
|
],
|
||||||
|
"homepage": "https://dev/cm",
|
||||||
|
"author": {
|
||||||
|
"name": "devcm",
|
||||||
|
"url": "https://dev/cm"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"devDependencies": {
|
||||||
|
"@iconify/json": "^2.1.132",
|
||||||
|
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||||
|
"@tailwindcss/typography": "^0.5.7",
|
||||||
|
"@types/alpinejs": "^3.7.1",
|
||||||
|
"@types/node": "^16.18.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.42.0",
|
||||||
|
"@typescript-eslint/parser": "^5.42.0",
|
||||||
|
"autoprefixer": "^10.4.13",
|
||||||
|
"eslint": "^8.26.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"postcss": "^8.4.31",
|
||||||
|
"prettier": "^2.7.1",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||||
|
"sass": "^1.56.1",
|
||||||
|
"tailwindcss": "^3.2.1",
|
||||||
|
"tailwindcss-plugin-icons": "^2.1.1",
|
||||||
|
"typescript": "^4.8.4",
|
||||||
|
"vite": "^3.2.2",
|
||||||
|
"vite-plugin-purge-icons": "^0.9.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@iconify/iconify": "^3.0.0",
|
||||||
|
"alpinejs": "^3.10.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
+2421
文件差异内容过多而无法显示
加载差异
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [require("prettier-plugin-tailwindcss")],
|
||||||
|
printWidth: 120,
|
||||||
|
tabWidth: 2,
|
||||||
|
useTabs: false,
|
||||||
|
endOfLine: "lf",
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export const typewriterEffect = (selectors: string) => {
|
||||||
|
const typedTextContainer = document.querySelector<HTMLDivElement>(selectors)
|
||||||
|
|
||||||
|
if (!typedTextContainer) return
|
||||||
|
|
||||||
|
const text = typedTextContainer.innerText
|
||||||
|
|
||||||
|
// 清楚原有的文本
|
||||||
|
typedTextContainer.innerText = ''
|
||||||
|
|
||||||
|
// 实现打字机效果
|
||||||
|
let i = 0
|
||||||
|
|
||||||
|
const typewriter = () => {
|
||||||
|
if (i >= text.length) return
|
||||||
|
|
||||||
|
typedTextContainer.innerText += text.charAt(i++)
|
||||||
|
|
||||||
|
setTimeout(typewriter, Math.random() * 200 + 50)
|
||||||
|
}
|
||||||
|
|
||||||
|
typewriter()
|
||||||
|
}
|
||||||
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
@@ -0,0 +1,18 @@
|
|||||||
|
import './styles/tailwind.css'
|
||||||
|
import './styles/style.scss'
|
||||||
|
import './styles/theme.scss'
|
||||||
|
import './styles/font-hack.scss'
|
||||||
|
|
||||||
|
import Alpine from 'alpinejs'
|
||||||
|
import upvote from './upvote'
|
||||||
|
import {typewriterEffect} from './custom'
|
||||||
|
|
||||||
|
window.Alpine = Alpine
|
||||||
|
|
||||||
|
Alpine.data('upvote', upvote)
|
||||||
|
|
||||||
|
Alpine.start()
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
typewriterEffect('.typed-text')
|
||||||
|
})
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
.button-container {
|
||||||
|
display: table;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
.button,
|
||||||
|
a.button {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 8px 18px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
appearance: none;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
/* variants */
|
||||||
|
|
||||||
|
&.outline {
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 8px 18px;
|
||||||
|
|
||||||
|
:hover {
|
||||||
|
transform: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
box-shadow: 0 4px 6px rgba(50, 50, 93, .11), 0 1px 3px rgba(0, 0, 0, .08);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 2px 6px rgba(50, 50, 93, .21), 0 1px 3px rgba(0, 0, 0, .08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.link {
|
||||||
|
background: none;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* sizes */
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
font-size: .8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.wide {
|
||||||
|
min-width: 200px;
|
||||||
|
padding: 14px 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.read-more,
|
||||||
|
a.read-more:hover,
|
||||||
|
a.read-more:active {
|
||||||
|
display: inline-flex;
|
||||||
|
background: none;
|
||||||
|
box-shadow: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 20px 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-toolbar {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.toolbar-item a {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 3px 8px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
appearance: none;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*!
|
||||||
|
* Hack typeface https://github.com/source-foundry/Hack
|
||||||
|
* License: https://github.com/source-foundry/Hack/blob/master/LICENSE.md
|
||||||
|
*/
|
||||||
|
/* FONT PATHS
|
||||||
|
* -------------------------- */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
/* Use full version (not a subset) for unicode icon support */
|
||||||
|
src: url('fonts/hack-regular.woff2?sha=3114f1256') format('woff2'), url('fonts/hack-regular.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('fonts/hack-bold-subset.woff2?sha=3114f1256') format('woff2'), url('fonts/hack-bold-subset.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('fonts/hack-italic-subset.woff2?sha=3114f1256') format('woff2'), url('fonts/hack-italic-webfont.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('fonts/hack-bolditalic-subset.woff2?sha=3114f1256') format('woff2'), url('fonts/hack-bolditalic-subset.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*!
|
||||||
|
* Hack typeface https://github.com/source-foundry/Hack
|
||||||
|
* License: https://github.com/source-foundry/Hack/blob/master/LICENSE.md
|
||||||
|
*/
|
||||||
|
/* FONT PATHS
|
||||||
|
* -------------------------- */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('../fonts/hack-regular.woff2?sha=3114f1256') format('woff2'), url('../fonts/hack-regular.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('../fonts/hack-bold.woff2?sha=3114f1256') format('woff2'), url('../fonts/hack-bold.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('../fonts/hack-italic.woff2?sha=3114f1256') format('woff2'), url('../fonts/hack-italic.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Hack';
|
||||||
|
src: url('../fonts/hack-bolditalic.woff2?sha=3114f1256') format('woff2'), url('../fonts/hack-bolditalic.woff?sha=3114f1256') format('woff');
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
@import "variables";
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
padding: 40px 0 0 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
opacity: .5;
|
||||||
|
|
||||||
|
&__inner {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0;
|
||||||
|
width: 760px;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
@media (max-width: $tablet-max-width) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyright {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--light-color-secondary);
|
||||||
|
|
||||||
|
&--user {
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > *:first-child:not(:only-child) {
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
@media (max-width: $tablet-max-width) {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $tablet-max-width) {
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyright-theme-sep {
|
||||||
|
@media (max-width: $tablet-max-width) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copyright-theme {
|
||||||
|
@media (max-width: $tablet-max-width) {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.beian {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
flex-basis: 100%;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--light-color-secondary);
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
@import "variables";
|
||||||
|
|
||||||
|
@mixin menu {
|
||||||
|
position: absolute;
|
||||||
|
background: var(--background);
|
||||||
|
box-shadow: var(--shadow);
|
||||||
|
color: white;
|
||||||
|
border: 2px solid;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
list-style: none;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&__inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__logo {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
background: repeating-linear-gradient(90deg, var(--foreground), var(--foreground) 2px, transparent 0, transparent 16px);
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
margin: 20px 0;
|
||||||
|
|
||||||
|
&__inner {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
color: var(--green);
|
||||||
|
|
||||||
|
li {
|
||||||
|
&.active {
|
||||||
|
color: var(--cyan);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-of-type) {
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__sub-inner {
|
||||||
|
position: relative;
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&:not(:only-child) {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-more {
|
||||||
|
@include menu;
|
||||||
|
top: 35px;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
&-trigger {
|
||||||
|
color: var(--foreground);
|
||||||
|
user-select: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
.logo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
text-decoration: none;
|
||||||
|
background: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,283 @@
|
|||||||
|
@import "variables";
|
||||||
|
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
*:before,
|
||||||
|
*:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: Ark-Pixel-12-proportional-zh_cn;
|
||||||
|
src: url("../fonts/fusion-pixel-12px-proportional-zh_hans.woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: Hack, Monaco, Consolas, 'Ubuntu Mono', PingHei, 'PingFang SC', 'Microsoft YaHei', monospace;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.54;
|
||||||
|
background-color: var(--background);
|
||||||
|
color: var(--foreground);
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
|
||||||
|
@media (max-width: $phone-max-width) {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pixel_style {
|
||||||
|
font-family: Ark-Pixel-12-proportional-zh_cn, serif;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4, h5, h6 {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
margin-top: .5rem;
|
||||||
|
margin-bottom: .25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.center {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: .75rem;
|
||||||
|
margin-bottom: .75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
display: table;
|
||||||
|
max-width: 100%;
|
||||||
|
margin: 25px 0;
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
img {
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.center {
|
||||||
|
img {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
img {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
figcaption {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
background: var(--foreground);
|
||||||
|
color: var(--background);
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: Hack, Monaco, Consolas, 'Ubuntu Mono', PingHei, 'PingFang SC', 'Microsoft YaHei', monospace;
|
||||||
|
font-feature-settings: normal;
|
||||||
|
background: var(--selectionBackground);
|
||||||
|
padding: 1px 4px;
|
||||||
|
margin: 0 4px;
|
||||||
|
font-size: .95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: Hack, Monaco, Consolas, 'Ubuntu Mono', PingHei, 'PingFang SC', 'Microsoft YaHei', monospace;
|
||||||
|
font-size: .95rem;
|
||||||
|
overflow: auto;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, .1);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, .1);
|
||||||
|
|
||||||
|
@media (max-width: $phone-max-width) {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
border-top: 1px solid var(--foreground);
|
||||||
|
border-bottom: 1px solid var(--foreground);
|
||||||
|
border-left: .5rem solid var(--foreground);
|
||||||
|
margin: 2rem 0;
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
@media (max-width: $phone-max-width) {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content: '”';
|
||||||
|
font-family: Georgia, serif;
|
||||||
|
font-size: 3.875rem;
|
||||||
|
position: absolute;
|
||||||
|
left: -40px;
|
||||||
|
top: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p:first-of-type {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p:last-of-type {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
table-layout: fixed;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
margin: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table, th, td {
|
||||||
|
border: 1px dashed var(--foreground);
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
margin-left: 30px;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-top: .125rem;
|
||||||
|
margin-bottom: .125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $phone-max-width) {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ol {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 40px;
|
||||||
|
max-width: 864px;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
@media (max-width: $phone-max-width) {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
background: var(--foreground);
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#theme-toggle {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--foreground);
|
||||||
|
margin: 0 0 0 .5rem;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#search-button {
|
||||||
|
background-color: transparent;
|
||||||
|
color: var(--foreground);
|
||||||
|
margin: 0 0 0 .5rem;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
@import 'variables';
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
display: flex;
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
margin: 100px 0 20px;
|
||||||
|
|
||||||
|
&-h {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: var(--background);
|
||||||
|
font-size: .8rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .1em;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 15px;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__buttons {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
@media (max-width: $phone-max-width) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
max-width: 40%;
|
||||||
|
padding: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
appearance: none;
|
||||||
|
|
||||||
|
@media (max-width: $phone-max-width) {
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ .button {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: flex;
|
||||||
|
padding: 8px 16px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
@import "variables";
|
||||||
|
|
||||||
|
.index-content {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.framed {
|
||||||
|
border: 1px solid var(--red);
|
||||||
|
color: var(--red);
|
||||||
|
padding: 20px;
|
||||||
|
*:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
*:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.posts {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 20px 0;
|
||||||
|
|
||||||
|
@media (max-width: $tablet-max-width) {
|
||||||
|
max-width: 660px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-of-type) {
|
||||||
|
border-bottom: 1px solid var(--foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
%meta {
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: var(--brightBlue);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-meta {
|
||||||
|
@extend %meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-meta-inline {
|
||||||
|
@extend %meta;
|
||||||
|
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-title {
|
||||||
|
--border: 2px dashed var(--blue);
|
||||||
|
position: relative;
|
||||||
|
color: var(--blue);
|
||||||
|
margin: 0 0 15px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
border-bottom: var(--border);
|
||||||
|
font-weight: normal;
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
%tags {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tags {
|
||||||
|
@extend %tags;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tag {
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
|
||||||
|
&-tags-inline {
|
||||||
|
@extend %tags;
|
||||||
|
|
||||||
|
display: inline;
|
||||||
|
|
||||||
|
@media (max-width: $phone-max-width) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
margin-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-cover {
|
||||||
|
border: 20px solid var(--foreground);
|
||||||
|
background: transparent;
|
||||||
|
margin: 40px 0;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
|
@media (max-width: $phone-max-width) {
|
||||||
|
padding: 10px;
|
||||||
|
border-width: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li:before {
|
||||||
|
content: '►';
|
||||||
|
position: absolute;
|
||||||
|
left: -20px;
|
||||||
|
color: var(--foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.post--regulation {
|
||||||
|
h1 {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
&+ h2 {
|
||||||
|
margin-top: -10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-list {
|
||||||
|
|
||||||
|
padding: .25rem 0;
|
||||||
|
|
||||||
|
.post-date {
|
||||||
|
color: var(--foreground);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-list-title {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post-tag {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
@import 'buttons';
|
||||||
|
@import 'header';
|
||||||
|
@import 'logo';
|
||||||
|
@import 'main';
|
||||||
|
@import 'post';
|
||||||
|
@import 'pagination';
|
||||||
|
@import 'footer';
|
||||||
|
@import 'typed-text';
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind utilities;
|
||||||
|
@tailwind components;
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
html[data-color-scheme='light'] {
|
||||||
|
--black: #3e3e3e;
|
||||||
|
--red: #970b16;
|
||||||
|
--green: #07962a;
|
||||||
|
--yellow: #f8eec7;
|
||||||
|
--blue: #003e8a;
|
||||||
|
--purple: #e94691;
|
||||||
|
--cyan: #89d1ec;
|
||||||
|
--white: #ffffff;
|
||||||
|
--brightBlack: #666666;
|
||||||
|
--brightRed: #de0000;
|
||||||
|
--brightGreen: #87d5a2;
|
||||||
|
--brightYellow: #f1d007;
|
||||||
|
--brightBlue: #2e6cba;
|
||||||
|
--brightPurple: #ffa29f;
|
||||||
|
--brightCyan: #1cfafe;
|
||||||
|
--brightWhite: #ffffff;
|
||||||
|
--background: #f4f4f4;
|
||||||
|
--foreground: #3e3e3e;
|
||||||
|
--cursorColor: #3f3f3f;
|
||||||
|
--selectionBackground: #a9c1e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-color-scheme='dark'] {
|
||||||
|
--black: #000000;
|
||||||
|
--red: #f78166;
|
||||||
|
--green: #56d364;
|
||||||
|
--yellow: #e3b341;
|
||||||
|
--blue: #6ca4f8;
|
||||||
|
--purple: #db61a2;
|
||||||
|
--cyan: #2b7489;
|
||||||
|
--white: #ffffff;
|
||||||
|
--brightBlack: #4d4d4d;
|
||||||
|
--brightRed: #f78166;
|
||||||
|
--brightGreen: #56d364;
|
||||||
|
--brightYellow: #e3b341;
|
||||||
|
--brightBlue: #6ca4f8;
|
||||||
|
--brightPurple: #db61a2;
|
||||||
|
--brightCyan: #2b7489;
|
||||||
|
--brightWhite: #ffffff;
|
||||||
|
--background: #101216;
|
||||||
|
--foreground: #8b949e;
|
||||||
|
--cursorColor: #c9d1d9;
|
||||||
|
--selectionBackground: #3b5070;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-color-scheme='light'] .d-block-light,
|
||||||
|
[data-color-scheme='dark'] .d-block-dark {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/* 光标样式 */
|
||||||
|
.typed-text::after {
|
||||||
|
content: '|';
|
||||||
|
display: inline-block;
|
||||||
|
opacity: 0;
|
||||||
|
animation: blink 0.7s infinite alternate; /* 闪烁动画 */
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 闪烁动画 */
|
||||||
|
@keyframes blink {
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
$phone-max-width: 683px;
|
||||||
|
$tablet-max-width: 899px;
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
interface upvoteState {
|
||||||
|
upvotedNames: string[];
|
||||||
|
|
||||||
|
init(): void;
|
||||||
|
|
||||||
|
upvoted(id: string): boolean;
|
||||||
|
|
||||||
|
handleUpvote(name: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default (key: string, group: string, plural: string): upvoteState => ({
|
||||||
|
upvotedNames: [],
|
||||||
|
init() {
|
||||||
|
this.upvotedNames = JSON.parse(localStorage.getItem(`walker.upvoted.${key}.names`) || '[]')
|
||||||
|
},
|
||||||
|
upvoted(id: string) {
|
||||||
|
return this.upvotedNames.includes(id)
|
||||||
|
},
|
||||||
|
async handleUpvote(name) {
|
||||||
|
if (this.upvoted(name)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const xhr = new XMLHttpRequest()
|
||||||
|
|
||||||
|
xhr.open('POST', '/apis/api.halo.run/v1alpha1/trackers/upvote')
|
||||||
|
xhr.setRequestHeader('Content-Type', 'application/json')
|
||||||
|
|
||||||
|
xhr.onload = () => {
|
||||||
|
this.upvotedNames = [...this.upvotedNames, name]
|
||||||
|
localStorage.setItem(`walker.upvoted.${key}.names`, JSON.stringify(this.upvotedNames))
|
||||||
|
|
||||||
|
const upvoteNode = document.querySelector('[data-upvote-' + key + '-name="' + name + '"]')
|
||||||
|
|
||||||
|
if (!upvoteNode) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const upvoteCount = parseInt(upvoteNode.textContent || '0')
|
||||||
|
upvoteNode.textContent = upvoteCount + 1 + ''
|
||||||
|
}
|
||||||
|
xhr.onerror = function () {
|
||||||
|
alert('网络请求失败,请稍后再试')
|
||||||
|
}
|
||||||
|
xhr.send(
|
||||||
|
JSON.stringify({
|
||||||
|
group: group,
|
||||||
|
plural: plural,
|
||||||
|
name: name,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
const { Icons } = require("tailwindcss-plugin-icons");
|
||||||
|
module.exports = {
|
||||||
|
content: ["./templates/**/*.html", "./src/main.ts"],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
require("@tailwindcss/typography"),
|
||||||
|
require("@tailwindcss/aspect-ratio"),
|
||||||
|
Icons(() => ({
|
||||||
|
pixelarticons: {
|
||||||
|
includeAll: true,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
darkMode: ['class', '[data-color-scheme="dark"]'],
|
||||||
|
safelist: [
|
||||||
|
"prose-sm",
|
||||||
|
"prose-base",
|
||||||
|
"prose-lg",
|
||||||
|
"prose-xl",
|
||||||
|
"prose-2xl",
|
||||||
|
"prose-gray",
|
||||||
|
"prose-slate",
|
||||||
|
"prose-zinc",
|
||||||
|
"prose-neutral",
|
||||||
|
"prose-stone",
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<div class="content">
|
||||||
|
<div class="post">
|
||||||
|
<h1 class="post-title" th:text="文章归档">文章归档</h1>
|
||||||
|
<th:block th:each="archive : ${archives.items}">
|
||||||
|
<ul>
|
||||||
|
<th:block th:each="month : ${archive.months}">
|
||||||
|
<li class="post-list" th:each="post : ${month.posts}">
|
||||||
|
<a th:href="${post.status.permalink}">
|
||||||
|
<span class="post-date" th:text="${#dates.format(post.spec.publishTime,'yyyy-MM-dd')}"
|
||||||
|
>发布时间</span
|
||||||
|
>
|
||||||
|
:: <span class="post-list-title" th:text="${post.spec.title}">文章标题</span></a>
|
||||||
|
<span class="post-tags-inline">
|
||||||
|
::
|
||||||
|
<span th:each="tag : ${post.tags}">
|
||||||
|
<a
|
||||||
|
th:href="${tag.status.permalink}"
|
||||||
|
th:title="${tag.spec.displayName}"
|
||||||
|
th:text="'#'+${tag.spec.displayName}"
|
||||||
|
class="post-tag"
|
||||||
|
>#Tag
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</th:block>
|
||||||
|
</ul>
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
<div class="pagination" th:if="${archives.hasPrevious() || archives.hasNext()}">
|
||||||
|
<div class="pagination__buttons">
|
||||||
|
<span class="button previous" th:if="${archives.hasPrevious()}">
|
||||||
|
<a th:href="@{${archives.prevUrl}}">
|
||||||
|
<span class="button__icon">< </span>
|
||||||
|
<span class="button__text">上一页</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="button next" th:if="${archives.hasNext()}">
|
||||||
|
<a th:href="@{${archives.nextUrl}}">
|
||||||
|
<span class="button__text">下一页</span>
|
||||||
|
<span class="button__icon"> ></span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
二进制文件未显示。
|
之后 宽度: | 高度: | 大小: 11 KiB |
@@ -0,0 +1,16 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<div class="content">
|
||||||
|
<div class="post">
|
||||||
|
<h1 class="post-title">所有分类</h1>
|
||||||
|
<ul>
|
||||||
|
<li th:replace="~{modules/category-tree :: single(categories=${categories})}" />
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<div class="content">
|
||||||
|
<div class="post">
|
||||||
|
<h1
|
||||||
|
class="post-title"
|
||||||
|
th:text="'分类:'+${category.spec.displayName}+' ('+${category.status.visiblePostCount}+' 篇文章)'"
|
||||||
|
>
|
||||||
|
分类:分类名 (n 篇文章)
|
||||||
|
</h1>
|
||||||
|
<a href="/categories"> 所有分类 </a>
|
||||||
|
<ul>
|
||||||
|
<li class="post-list" th:each="post : ${posts.items}">
|
||||||
|
<a th:href="${post.status.permalink}">
|
||||||
|
<span class="post-date" th:text="${#dates.format(post.spec.publishTime,'yyyy-MM-dd')}"
|
||||||
|
>发布时间</span
|
||||||
|
>
|
||||||
|
:: <span class="post-list-title" th:text="${post.spec.title}">文章标题</span></a>
|
||||||
|
<span class="post-tags-inline">
|
||||||
|
::
|
||||||
|
<span th:each="tag : ${post.tags}">
|
||||||
|
<a
|
||||||
|
th:href="${tag.status.permalink}"
|
||||||
|
th:title="${tag.spec.displayName}"
|
||||||
|
th:text="'#'+${tag.spec.displayName}"
|
||||||
|
class="post-tag"
|
||||||
|
>#Tag
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="pagination" th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||||
|
<div class="pagination__buttons">
|
||||||
|
<span class="button previous" th:if="${posts.hasPrevious()}">
|
||||||
|
<a th:href="@{${posts.prevUrl}}">
|
||||||
|
<span class="button__icon"><</span>
|
||||||
|
<span class="button__text">上一页</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="button next" th:if="${posts.hasNext()}">
|
||||||
|
<a th:href="@{${posts.nextUrl}}">
|
||||||
|
<span class="button__text">下一页</span>
|
||||||
|
<span class="button__icon">></span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<div class="content">
|
||||||
|
<div class="index-content framed">
|
||||||
|
<h1 class="typed-text" th:text="${theme.config.index.index_notice_title}"></h1>
|
||||||
|
<div th:utext="${theme.config.index.index_notice_content}"></div>
|
||||||
|
</div>
|
||||||
|
<div class="posts">
|
||||||
|
<div class="post on-list" th:each="post : ${posts.items}">
|
||||||
|
<h1 class="post-title">
|
||||||
|
<a th:text="${post.spec.title}" th:href="${post.status.permalink}">Post Title</a>
|
||||||
|
</h1>
|
||||||
|
<div class="post-meta">
|
||||||
|
<span class="post-date" th:text="${#dates.format(post.spec.publishTime,'yyyy-MM-dd')}">
|
||||||
|
Post CreateTime
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="post-author"
|
||||||
|
th:with="contributor = ${post.contributors[0]}"
|
||||||
|
th:text="${':: '+contributor.displayName}"
|
||||||
|
>:: Author</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<span class="post-tags-inline" th:each="tag : ${post.tags}">
|
||||||
|
<a
|
||||||
|
th:href="${tag.status.permalink}"
|
||||||
|
th:title="${tag.spec.displayName}"
|
||||||
|
th:text="'#'+${tag.spec.displayName}"
|
||||||
|
class="post-tag"
|
||||||
|
>#Tag
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<div class="post-content" th:text="${post.status.excerpt}">Post Excerpt...</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<a class="read-more button" th:href="${post.status.permalink}">阅读更多 ></a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pagination" th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||||
|
<div class="pagination__buttons">
|
||||||
|
<span class="button previous" th:if="${posts.hasPrevious()}">
|
||||||
|
<a th:href="@{${posts.prevUrl}}">
|
||||||
|
<span class="button__icon">< </span>
|
||||||
|
<span class="button__text">上一页</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="button next" th:if="${posts.hasNext()}">
|
||||||
|
<a th:href="@{${posts.nextUrl}}">
|
||||||
|
<span class="button__text">下一页</span>
|
||||||
|
<span class="button__icon"> ></span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<script th:src="@{/assets/libs/pixelit.js}"></script>
|
||||||
|
<div>
|
||||||
|
<section class="">
|
||||||
|
<div class="">
|
||||||
|
<div class="mb-6" th:each="group : ${groups}">
|
||||||
|
<div class="">
|
||||||
|
<h2
|
||||||
|
class="post-title"
|
||||||
|
th:text="${!#strings.isEmpty(group.spec.displayName) ? group.spec.displayName:'友情链接'}"
|
||||||
|
>
|
||||||
|
链接分组
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
<div class="">
|
||||||
|
<a th:each="link : ${group.links}" th:href="${link.spec.url}" target="_blank" class="mb-2 block">
|
||||||
|
<div class="flex bg-[#a9c1e2] dark:bg-[#3b5070] p-1">
|
||||||
|
<div class="">
|
||||||
|
<img class="w-16" src="sky.jpg" th:src="${link.spec.logo}" id="pixelitimg" th:alt="${link.spec.displayName}">
|
||||||
|
</div>
|
||||||
|
<div class="ml-4 flex flex-col justify-around">
|
||||||
|
<div class="m-0 text-[#003e8a] dark:text-[#6ca4f8]"><span th:text="${link.spec.displayName}"></span><span th:text="| (${link.spec.url})|"></span></div>
|
||||||
|
<div class="m-0 text-[#3e3e3e] dark:text-[#8b949e]" th:text="${link.spec.description}"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<script>
|
||||||
|
//create object
|
||||||
|
const px = new pixelit();
|
||||||
|
px.draw().pixelate();
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<ul th:fragment="next (categories)">
|
||||||
|
<li th:fragment="single (categories)" th:each="category : ${categories}">
|
||||||
|
<a th:href="@{${category.status.permalink}}">
|
||||||
|
<span th:text="${category.spec.displayName}+' ('+${category.status.visiblePostCount}+' 篇文章)'"> </span>
|
||||||
|
</a>
|
||||||
|
<th:block th:if="${not #lists.isEmpty(category.children)}">
|
||||||
|
<th:block th:replace="~{modules/category-tree :: next (categories=${category.children})}"></th:block>
|
||||||
|
</th:block>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<footer class="footer">
|
||||||
|
<div class="footer__inner">
|
||||||
|
<div class="copyright">
|
||||||
|
<span
|
||||||
|
>© <span th:text="${#dates.year(#dates.createNow())}">2024</span> Powered by
|
||||||
|
<a th:href="${theme.config.footer.footer_powered_link}">cccc</a></span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="beian">
|
||||||
|
<span th:if="${not #strings.isEmpty(theme.config.beian.icp_text)}">
|
||||||
|
<a
|
||||||
|
href="https://beian.miit.gov.cn/"
|
||||||
|
target="_blank"
|
||||||
|
th:href="${theme.config.beian.icp_link}"
|
||||||
|
th:text="${theme.config.beian.icp_text}"
|
||||||
|
></a
|
||||||
|
></span>
|
||||||
|
<span th:if="${not #strings.isEmpty(theme.config.beian.gongan_text)}">
|
||||||
|
<img th:src="@{/assets/image/gongan.png}" style="display:inline-block; height: 1.2em;">
|
||||||
|
<a
|
||||||
|
href="https://www.beian.gov.cn/"
|
||||||
|
target="_blank"
|
||||||
|
th:href="${theme.config.beian.gongan_link}"
|
||||||
|
th:text="${theme.config.beian.gongan_text}"
|
||||||
|
></a
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<header class="header">
|
||||||
|
<div th:fragment="header" th:with="menu = ${menuFinder.getPrimary()}">
|
||||||
|
<div class="header__inner">
|
||||||
|
<div class="header__logo">
|
||||||
|
<a href="/" style="text-decoration: none">
|
||||||
|
<div class="logo" th:text="${site.title}">Blog Title</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
id="search-button"
|
||||||
|
type="button"
|
||||||
|
th:if="${pluginFinder.available('PluginSearchWidget')}"
|
||||||
|
onclick="SearchWidget.open()"
|
||||||
|
>
|
||||||
|
<th:block th:unless="${theme.config.basic.pixel_style}">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1.5rem" height="1.5rem" viewBox="0 0 24 24">
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="m19.6 21l-6.3-6.3q-.75.6-1.725.95T9.5 16q-2.725 0-4.612-1.888T3 9.5q0-2.725 1.888-4.612T9.5 3q2.725 0 4.612 1.888T16 9.5q0 1.1-.35 2.075T14.7 13.3l6.3 6.3l-1.4 1.4ZM9.5 14q1.875 0 3.188-1.313T14 9.5q0-1.875-1.313-3.188T9.5 5Q7.625 5 6.312 6.313T5 9.5q0 1.875 1.313 3.188T9.5 14Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${theme.config.basic.pixel_style}">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1.5rem" height="1.5rem" viewBox="0 0 24 24"><path fill="currentColor" d="M6 2h8v2H6zM4 6V4h2v2zm0 8H2V6h2zm2 2H4v-2h2zm8 0v2H6v-2zm2-2h-2v2h2v2h2v2h2v2h2v-2h-2v-2h-2v-2h-2zm0-8h2v8h-2zm0 0V4h-2v2z"/></svg>
|
||||||
|
</th:block>
|
||||||
|
</button>
|
||||||
|
<button id="theme-toggle" type="button">
|
||||||
|
<th:block th:unless="${theme.config.basic.pixel_style}">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
style="vertical-align: -0.125em"
|
||||||
|
width="1.5rem"
|
||||||
|
height="1.5rem"
|
||||||
|
preserveAspectRatio="xMidYMid meet"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M7.5 2c-1.79 1.15-3 3.18-3 5.5s1.21 4.35 3.03 5.5C4.46 13 2 10.54 2 7.5A5.5 5.5 0 0 1 7.5 2m11.57 1.5l1.43 1.43L4.93 20.5L3.5 19.07L19.07 3.5m-6.18 2.43L11.41 5L9.97 6l.42-1.7L9 3.24l1.75-.12l.58-1.65L12 3.1l1.73.03l-1.35 1.13l.51 1.67m-3.3 3.61l-1.16-.73l-1.12.78l.34-1.32l-1.09-.83l1.36-.09l.45-1.29l.51 1.27l1.36.03l-1.05.87l.4 1.31M19 13.5a5.5 5.5 0 0 1-5.5 5.5c-1.22 0-2.35-.4-3.26-1.07l7.69-7.69c.67.91 1.07 2.04 1.07 3.26m-4.4 6.58l2.77-1.15l-.24 3.35l-2.53-2.2m4.33-2.7l1.15-2.77l2.2 2.54l-3.35.23m1.15-4.96l-1.14-2.78l3.34.24l-2.2 2.54M9.63 18.93l2.77 1.15l-2.53 2.19l-.24-3.34Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${theme.config.basic.pixel_style}">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1.5rem" height="1.5rem" viewBox="0 0 24 24"><path fill="currentColor" d="M11 0h2v4h-2zm1 12H8v2H4v2H2v4h2v2h10v-2h2v-4h-2v-2h-2zm0 2v2h2v4H4v-4h4v2h2v-2H8v-2zM8 6h6v2H8zm0 2v2H6V8zm8 2h-2V8h2zm0 0h2v2h-2zm4-8h2v2h-2zm0 2v2h-2V4zM2 2h2v2H2zm2 2h2v2H4zm20 7h-4v2h4z"/></svg>
|
||||||
|
</th:block>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<nav class="menu">
|
||||||
|
<ul th:if="${menu != null} and ${not #lists.isEmpty(menu.menuItems)}" class="menu__inner menu__inner--desktop">
|
||||||
|
<li th:each="menuItem : ${menu.menuItems}">
|
||||||
|
<a
|
||||||
|
class="text-gray-600 hover:text-blue-600"
|
||||||
|
th:href="${menuItem.status.href}"
|
||||||
|
th:text="${menuItem.status.displayName}"
|
||||||
|
></a>
|
||||||
|
<ul
|
||||||
|
th:if="${not #lists.isEmpty(menuItem.children)}"
|
||||||
|
@mouseenter="open()"
|
||||||
|
@mouseleave="close()"
|
||||||
|
class="menu__sub-inner-more hidden"
|
||||||
|
>
|
||||||
|
<li th:each="childMenuItem : ${menuItem.children}">
|
||||||
|
<a th:href="${childMenuItem.status.href} " th:text="${childMenuItem.status.displayName} "></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<script>
|
||||||
|
var toggle = document.getElementById("theme-toggle");
|
||||||
|
|
||||||
|
var storedTheme =
|
||||||
|
localStorage.getItem("theme") || (window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
|
||||||
|
if (storedTheme) document.documentElement.setAttribute("data-color-scheme", storedTheme);
|
||||||
|
|
||||||
|
toggle.onclick = function () {
|
||||||
|
var currentTheme = document.documentElement.getAttribute("data-color-scheme");
|
||||||
|
var targetTheme = "light";
|
||||||
|
|
||||||
|
if (currentTheme === "light") {
|
||||||
|
targetTheme = "dark";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.setAttribute("data-color-scheme", targetTheme);
|
||||||
|
localStorage.setItem("theme", targetTheme);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" th:fragment="html (header, content, footer)">
|
||||||
|
<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 th:text="${site.title}"></title>
|
||||||
|
<link rel="stylesheet" th:href="@{/assets/dist/style.css}" href="./assets/dist/style.css" />
|
||||||
|
</head>
|
||||||
|
<!-- 根据情况判断是否添加开启像素化样式 -->
|
||||||
|
<body class="main" th:classappend="${theme.config.basic.pixel_style} ? 'pixel_style' : '' ">
|
||||||
|
<div class="center headings--one-size container">
|
||||||
|
<th:block th:if="${header != null}">
|
||||||
|
<th:block th:replace="${header}" />
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${header == null}">
|
||||||
|
<th:block th:replace="~{modules/header :: header(isHome = true)}" />
|
||||||
|
</th:block>
|
||||||
|
|
||||||
|
<th:block th:replace="${content}" />
|
||||||
|
|
||||||
|
<th:block th:if="${footer == null}">
|
||||||
|
<th:block th:replace="~{modules/footer}" />
|
||||||
|
</th:block>
|
||||||
|
<th:block th:if="${footer != null}">
|
||||||
|
<th:block th:replace="${footer}" />
|
||||||
|
</th:block>
|
||||||
|
</div>
|
||||||
|
<script th:src="@{/assets/dist/main.iife.js}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox/dist/css/glightbox.min.css" />
|
||||||
|
<script src="https://cdn.jsdelivr.net/gh/mcstudios/glightbox/dist/js/glightbox.min.js"></script>
|
||||||
|
<div class="content">
|
||||||
|
<div class="moments" x-data="upvote('moment','moment.halo.run','moments')">
|
||||||
|
<div
|
||||||
|
class="moment on-list post"
|
||||||
|
th:each="moment,iterStat : ${moments.items}"
|
||||||
|
th:with="content=${moment.spec.content}"
|
||||||
|
th:attr="x-data=|{name:'${moment.metadata.name}',showComment:false}|"
|
||||||
|
>
|
||||||
|
<div class="post-meta">
|
||||||
|
<span class="post-date" th:text="${#dates.format(moment.spec.releaseTime,'yyyy-MM-dd')}">
|
||||||
|
Moment CreateTime
|
||||||
|
</span>
|
||||||
|
<span class="post-author" th:with="owner = ${moment.owner}" th:text="${':: '+owner.displayName}">
|
||||||
|
:: Author
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="post-content" th:utext="${content.html}">Moment Content...</div>
|
||||||
|
<th:block th:with="medium=${moment.spec.content.medium}">
|
||||||
|
<div
|
||||||
|
th:unless="${#lists.isEmpty(moments)}"
|
||||||
|
class="moment-media mt-4 grid w-4/5 gap-2 sm:w-4/5"
|
||||||
|
th:classappend="${(#lists.size(medium) > 4 ? 'grid-cols-3' : '') + (#lists.size(medium) == 1 ? 'grid-cols-1' :'') + ((#lists.size(medium) > 1 && #lists.size(medium) < 5) ? 'grid-cols-2' :'')}"
|
||||||
|
>
|
||||||
|
<div class="" th:each="media : ${medium}">
|
||||||
|
<a th:class="|glightbox|" th:data-gallery="|gallery${iterStat.count}|" th:href="${media.url}">
|
||||||
|
<img
|
||||||
|
th:if="${#strings.equals(media.type,'PHOTO')}"
|
||||||
|
class="transform-gpu rounded-lg object-cover"
|
||||||
|
th:src="${media.url}"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<div th:if="${#strings.equals(media.type,'VIDEO')}" class="w-full">
|
||||||
|
<video controls th:src="${media.url}" class="rounded-lg object-cover"></video>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
<div class="mt-3 flex items-center gap-4">
|
||||||
|
<div
|
||||||
|
class="journal-likes inline-flex cursor-pointer items-center text-sm text-gray-400 transition-all hover:text-red-700"
|
||||||
|
x-bind:class="{'text-red-700': upvoted(name)}"
|
||||||
|
@click="handleUpvote(name)"
|
||||||
|
>
|
||||||
|
<i class="!h-4 !w-4 i-pixelarticons-heart"></i>
|
||||||
|
<span
|
||||||
|
class="ml-1"
|
||||||
|
th:attr="data-upvote-moment-name=${moment.metadata.name}"
|
||||||
|
th:text="${moment.stats.upvote}"
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="inline-flex cursor-pointer items-center text-sm text-gray-400 transition-all hover:text-black dark:hover:text-white"
|
||||||
|
:class="{'!text-black':showComment && storedTheme == 'light','!text-white':showComment && storedTheme == 'dark' }"
|
||||||
|
x-on:click="showComment = !showComment"
|
||||||
|
>
|
||||||
|
<i class="!h-4 !w-4 i-pixelarticons-comment"></i>
|
||||||
|
<span class="ml-1" th:text="${moment.stats.approvedComment}"> </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-2" x-show="showComment">
|
||||||
|
<halo:comment
|
||||||
|
group="moment.halo.run"
|
||||||
|
kind="Moment"
|
||||||
|
th:attr="name=${moment.metadata.name}"
|
||||||
|
colorScheme="storedTheme"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pagination" th:if="${moments.hasPrevious()} or ${moments.hasNext()}">
|
||||||
|
<div class="pagination__buttons">
|
||||||
|
<span class="button previous" th:if="${moments.hasPrevious()}">
|
||||||
|
<a th:href="@{${moments.prevUrl}}">
|
||||||
|
<div class="button__icon i-pixelarticons-chevron-left !h-6 !w-6"></div>
|
||||||
|
<span class="button__text">上一页</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="button next" th:if="${moments.hasNext()}">
|
||||||
|
<a th:href="@{${moments.nextUrl}}" class="items-center">
|
||||||
|
<span class="button__text">下一页</span>
|
||||||
|
<div class="button__icon i-pixelarticons-chevron-right !h-6 !w-6"></div>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
const lightbox = GLightbox({
|
||||||
|
loop: false,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<div class="post">
|
||||||
|
<h1 class="post-title" th:text="${singlePage.spec.title}">Post Title</h1>
|
||||||
|
<div class="post-meta">
|
||||||
|
<span class="post-date" th:text="${#dates.format(singlePage.spec.publishTime,'yyyy-MM-dd')}">
|
||||||
|
publishTime
|
||||||
|
</span>
|
||||||
|
<span class="post-author" th:text="${':: '+singlePage.owner.displayName}">:: Author</span>
|
||||||
|
</div>
|
||||||
|
<div class="post-content">
|
||||||
|
<div th:utext="${singlePage.content.content}">Post Content</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:if="${haloCommentEnabled}">
|
||||||
|
<h2>评论</h2>
|
||||||
|
<halo:comment
|
||||||
|
group="content.halo.run"
|
||||||
|
kind="SinglePage"
|
||||||
|
th:attr="name=${singlePage.metadata.name}"
|
||||||
|
colorScheme="localStorage.getItem('theme')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<div class="post">
|
||||||
|
<h1 class="post-title" th:text="${post.spec.title}">Post Title</h1>
|
||||||
|
<div class="post-meta">
|
||||||
|
<span class="post-date" th:text="${#dates.format(post.spec.publishTime,'yyyy-MM-dd')}"> publishTime </span>
|
||||||
|
<span class="post-author" th:text="${':: '+post.owner.displayName}">:: Author</span>
|
||||||
|
</div>
|
||||||
|
<span class="post-tags-inline" th:each="tag : ${post.tags}">
|
||||||
|
<a
|
||||||
|
th:href="${tag.status.permalink}"
|
||||||
|
th:title="${tag.spec.displayName}"
|
||||||
|
th:text="'#'+${tag.spec.displayName}"
|
||||||
|
class="post-tag"
|
||||||
|
>#Tag</a
|
||||||
|
>
|
||||||
|
</span>
|
||||||
|
<div class="post-content">
|
||||||
|
<div th:utext="${post.content.content}">Post Content</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div th:if="${haloCommentEnabled}">
|
||||||
|
<h2>评论</h2>
|
||||||
|
<halo:comment
|
||||||
|
group="content.halo.run"
|
||||||
|
kind="Post"
|
||||||
|
th:attr="name=${post.metadata.name}"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="pagination">
|
||||||
|
<div class="pagination__title">
|
||||||
|
<span class="pagination__title-h">其他文章</span>
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
<div class="pagination__buttons" th:with="postCursor=${postFinder.cursor(post.metadata?.name)}">
|
||||||
|
<span th:if="${postCursor.hasPrevious()}" class="button previous">
|
||||||
|
<a th:href="${postCursor.previous?.status?.permalink}">
|
||||||
|
<span class="button__icon">< </span>
|
||||||
|
<span class="button__text" th:text="${postCursor.previous?.spec?.title}">prevPost</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span th:if="${postCursor.hasNext()}" class="button next">
|
||||||
|
<a th:href="${postCursor.next?.status?.permalink}">
|
||||||
|
<span class="button__text" th:text="${postCursor.next?.spec?.title}">nextPost</span>
|
||||||
|
<span class="button__icon"> ></span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<div class="content">
|
||||||
|
<div class="post">
|
||||||
|
<h1
|
||||||
|
class="post-title"
|
||||||
|
th:text="'标签:'+${tag.spec.displayName}+' ('+${tag.status.visiblePostCount}+' 篇文章)'"
|
||||||
|
>
|
||||||
|
标签:标签名 (n 篇文章)
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<a href="/tags"> 所有标签 </a>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li class="post-list" th:each="post : ${posts.items}">
|
||||||
|
<a th:href="${post.status.permalink}">
|
||||||
|
<span class="post-date" th:text="${#dates.format(post.spec.publishTime,'yyyy-MM-dd')}"
|
||||||
|
>发布时间</span
|
||||||
|
>
|
||||||
|
:: <span class="post-list-title" th:text="${post.spec.title}">文章标题</span></a>
|
||||||
|
<span class="post-tags-inline">
|
||||||
|
::
|
||||||
|
<span th:each="tag : ${post.tags}">
|
||||||
|
<a
|
||||||
|
th:href="@{${post.status.permalink}}"
|
||||||
|
th:title="${tag.spec.displayName}"
|
||||||
|
th:text="'#'+${tag.spec.displayName}"
|
||||||
|
class="post-tag"
|
||||||
|
>#Tag
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="pagination" th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||||
|
<div class="pagination__buttons">
|
||||||
|
<span class="button previous" th:if="${posts.hasPrevious()}">
|
||||||
|
<a th:href="@{${posts.prevUrl}}">
|
||||||
|
<span class="button__icon"><</span>
|
||||||
|
<span class="button__text">上一页</span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
<span class="button next" th:if="${posts.hasNext()}">
|
||||||
|
<a th:href="@{${posts.nextUrl}}">
|
||||||
|
<span class="button__text">下一页</span>
|
||||||
|
<span class="button__icon">></span>
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html
|
||||||
|
xmlns:th="https://www.thymeleaf.org"
|
||||||
|
th:replace="~{modules/layout :: html(header = null, content = ~{::content}, footer = null)}"
|
||||||
|
>
|
||||||
|
<th:block th:fragment="content">
|
||||||
|
<div class="content">
|
||||||
|
<div class="post">
|
||||||
|
<h1 class="post-title">所有标签</h1>
|
||||||
|
<ul>
|
||||||
|
<li th:each="tag: ${tags}" class="tag-list">
|
||||||
|
<a
|
||||||
|
th:href="${tag.status.permalink}"
|
||||||
|
th:text="${tag.spec.displayName}+' ('+${tag.status.visiblePostCount}+' 篇文章)'"
|
||||||
|
>
|
||||||
|
标签名 (n 篇文章)
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</th:block>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "esnext",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"lib": ["esnext", "dom"],
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"strict": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
},
|
||||||
|
"include": ["src", "./env.d.ts"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import path from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
|
import PurgeIcons from "vite-plugin-purge-icons";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [PurgeIcons()],
|
||||||
|
build: {
|
||||||
|
outDir: fileURLToPath(new URL("./templates/assets/dist", import.meta.url)),
|
||||||
|
emptyOutDir: true,
|
||||||
|
lib: {
|
||||||
|
entry: path.resolve(__dirname, "src/main.ts"),
|
||||||
|
name: "main",
|
||||||
|
fileName: "main",
|
||||||
|
formats: ["iife"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
在新议题中引用
屏蔽一个用户