feat: 初始化 Halo 暗色模式插件
- Halo Plugin 后端(Java/Gradle),含 DarkModePlugin 主类和测试 - Vue 3 + TypeScript 前端 UI,包含主题切换组件和设置页面 - 暗色模式 CSS 变量和覆盖样式(布局/编辑器/表单/滚动条等) - 设计文档和调查文档 - Halo 插件/主题开发 Agent Skills Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1alpha1
|
||||
kind: Setting
|
||||
metadata:
|
||||
name: theme-minimal-setting
|
||||
spec:
|
||||
forms:
|
||||
- group: basic
|
||||
label: Basic Settings
|
||||
formSchema:
|
||||
- $formkit: text
|
||||
name: custom_footer
|
||||
label: Custom footer text
|
||||
value: ""
|
||||
placeholder: "e.g. Copyright © 2024 My Site"
|
||||
@@ -0,0 +1,25 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{layout :: html(head = null, content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="content">
|
||||
<h1>Archives</h1>
|
||||
<th:block th:each="archive : ${archives.items}">
|
||||
<h2 th:text="${archive.year}"></h2>
|
||||
<th:block th:each="month : ${archive.months}">
|
||||
<h3 th:text="|${archive.year}-${month.month}|"></h3>
|
||||
<ul>
|
||||
<li th:each="post : ${month.posts}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<nav th:if="${archives.hasPrevious() || archives.hasNext()}">
|
||||
<a th:if="${archives.hasPrevious()}" th:href="@{${archives.prevUrl}}">Previous</a>
|
||||
<span th:text="|${archives.page} / ${archives.totalPages}|"></span>
|
||||
<a th:if="${archives.hasNext()}" th:href="@{${archives.nextUrl}}">Next</a>
|
||||
</nav>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
body {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
font-family: system-ui, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #0070f3;
|
||||
}
|
||||
header {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
align-items: center;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
header a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
nav {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
footer {
|
||||
margin-top: 2rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid #eee;
|
||||
color: #666;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
article img {
|
||||
max-width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{layout :: html(head = ~{::head}, content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="head">
|
||||
<title th:text="|${author.spec.displayName} - ${site.title}|"></title>
|
||||
</th:block>
|
||||
<th:block th:fragment="content">
|
||||
<h1 th:text="${author.spec.displayName}"></h1>
|
||||
<p th:if="${not #strings.isEmpty(author.spec.bio)}" th:text="${author.spec.bio}"></p>
|
||||
|
||||
<ul>
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<nav th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a th:if="${posts.hasPrevious()}" th:href="@{${posts.prevUrl}}">Previous</a>
|
||||
<span th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a th:if="${posts.hasNext()}" th:href="@{${posts.nextUrl}}">Next</a>
|
||||
</nav>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{layout :: html(head = null, content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="content">
|
||||
<h1>Categories</h1>
|
||||
<ul>
|
||||
<li th:each="cat : ${categories.items}">
|
||||
<a
|
||||
th:href="@{${cat.status.permalink}}"
|
||||
th:text="|${cat.spec.displayName} (${cat.postCount})|"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{layout :: html(head = null, content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="content">
|
||||
<h1 th:text="|Category: ${category.spec.displayName}|"></h1>
|
||||
<ul>
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
</li>
|
||||
</ul>
|
||||
<nav th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a th:if="${posts.hasPrevious()}" th:href="@{${posts.prevUrl}}">Previous</a>
|
||||
<span th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a th:if="${posts.hasNext()}" th:href="@{${posts.nextUrl}}">Next</a>
|
||||
</nav>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -0,0 +1,19 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{layout :: html(head = null, content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="content">
|
||||
<ul>
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
</li>
|
||||
</ul>
|
||||
<nav th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a th:if="${posts.hasPrevious()}" th:href="@{${posts.prevUrl}}">Previous</a>
|
||||
<span th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a th:if="${posts.hasNext()}" th:href="@{${posts.nextUrl}}">Next</a>
|
||||
</nav>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
<!doctype html>
|
||||
<html lang="en" xmlns:th="https://www.thymeleaf.org" th:fragment="html (head, content)">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title th:text="${site.title}">Site Title</title>
|
||||
<link rel="stylesheet" th:href="@{/assets/css/style.css?v={v}(v=${theme.spec.version})}" />
|
||||
<th:block th:if="${head != null}">
|
||||
<th:block th:replace="${head}" />
|
||||
</th:block>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a th:href="@{/}" th:text="${site.title}"></a>
|
||||
<nav th:with="menu = ${menuFinder.getPrimary()}">
|
||||
<a
|
||||
th:each="item : ${menu.menuItems}"
|
||||
th:href="@{${item.status.href}}"
|
||||
th:text="${item.status.displayName}"
|
||||
th:target="${item.spec.target?.value}"
|
||||
></a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<th:block th:replace="${content}" />
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p th:text="${theme.config.basic.custom_footer ?: site.title}"></p>
|
||||
<halo:footer />
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{layout :: html(head = ~{::head}, content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="head">
|
||||
<title th:text="|${singlePage.spec.title} - ${site.title}|"></title>
|
||||
</th:block>
|
||||
<th:block th:fragment="content">
|
||||
<article>
|
||||
<h1 th:text="${singlePage.spec.title}"></h1>
|
||||
<div th:utext="${singlePage.content.content}"></div>
|
||||
</article>
|
||||
|
||||
<div th:if="${haloCommentEnabled}">
|
||||
<halo:comment
|
||||
group="content.halo.run"
|
||||
kind="SinglePage"
|
||||
th:attr="name=${singlePage.metadata.name}"
|
||||
/>
|
||||
</div>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{layout :: html(head = ~{::head}, content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="head">
|
||||
<title th:text="|${post.spec.title} - ${site.title}|"></title>
|
||||
</th:block>
|
||||
<th:block th:fragment="content">
|
||||
<article>
|
||||
<h1 th:text="${post.spec.title}"></h1>
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
<div th:utext="${post.content.content}"></div>
|
||||
<div th:if="${not #lists.isEmpty(post.tags)}">
|
||||
<a
|
||||
th:each="tag : ${post.tags}"
|
||||
th:href="@{${tag.status.permalink}}"
|
||||
th:text="${tag.spec.displayName}"
|
||||
></a>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<nav th:with="cursor = ${postFinder.cursor(post.metadata.name)}">
|
||||
<a
|
||||
th:if="${cursor.hasPrevious()}"
|
||||
th:href="@{${cursor.previous.status.permalink}}"
|
||||
th:text="${cursor.previous.spec.title}"
|
||||
>Previous</a
|
||||
>
|
||||
<a
|
||||
th:if="${cursor.hasNext()}"
|
||||
th:href="@{${cursor.next.status.permalink}}"
|
||||
th:text="${cursor.next.spec.title}"
|
||||
>Next</a
|
||||
>
|
||||
</nav>
|
||||
|
||||
<div th:if="${haloCommentEnabled}">
|
||||
<halo:comment group="content.halo.run" kind="Post" th:attr="name=${post.metadata.name}" />
|
||||
</div>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{layout :: html(head = null, content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="content">
|
||||
<h1 th:text="|Tag: ${tag.spec.displayName}|"></h1>
|
||||
<ul>
|
||||
<li th:each="post : ${posts.items}">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
</li>
|
||||
</ul>
|
||||
<nav th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a th:if="${posts.hasPrevious()}" th:href="@{${posts.prevUrl}}">Previous</a>
|
||||
<span th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a th:if="${posts.hasNext()}" th:href="@{${posts.nextUrl}}">Next</a>
|
||||
</nav>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html
|
||||
xmlns:th="https://www.thymeleaf.org"
|
||||
th:replace="~{layout :: html(head = null, content = ~{::content})}"
|
||||
>
|
||||
<th:block th:fragment="content">
|
||||
<h1>Tags</h1>
|
||||
<ul>
|
||||
<li th:each="tag : ${tags.items}">
|
||||
<a th:href="@{${tag.status.permalink}}" th:text="${tag.spec.displayName}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
</html>
|
||||
@@ -0,0 +1,20 @@
|
||||
apiVersion: theme.halo.run/v1alpha1
|
||||
kind: Theme
|
||||
metadata:
|
||||
name: theme-minimal
|
||||
spec:
|
||||
displayName: Minimal Theme
|
||||
author:
|
||||
name: Your Name
|
||||
website: https://example.com
|
||||
description: A minimal Halo theme starter
|
||||
logo: ""
|
||||
homepage: ""
|
||||
repo: ""
|
||||
issues: ""
|
||||
settingName: "theme-minimal-setting"
|
||||
configMapName: "theme-minimal-configmap"
|
||||
version: 1.0.0
|
||||
requires: ">=2.0.0"
|
||||
license:
|
||||
- name: "MIT"
|
||||
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
templates
|
||||
dist
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"build": "vite build && theme-package",
|
||||
"build-only": "vite build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@halo-dev/theme-package-cli": "^1.0.1",
|
||||
"@halo-dev/vite-plugin-halo-theme": "1.0.3",
|
||||
"vite": "^8.0.0"
|
||||
},
|
||||
"packageManager": "pnpm@10.33.0"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1alpha1
|
||||
kind: Setting
|
||||
metadata:
|
||||
name: theme-vite-setting
|
||||
spec:
|
||||
forms:
|
||||
- group: basic
|
||||
label: Basic Settings
|
||||
formSchema:
|
||||
- $formkit: text
|
||||
name: custom_footer
|
||||
label: Custom footer text
|
||||
value: ""
|
||||
placeholder: "e.g. Copyright © 2024 My Site"
|
||||
@@ -0,0 +1,32 @@
|
||||
<include src="layout.html">
|
||||
<template name="head">
|
||||
<title th:text="|Archives - ${site.title}|">Archives</title>
|
||||
</template>
|
||||
|
||||
<h1>Archives</h1>
|
||||
|
||||
<th:block th:each="archive : ${archives.items}">
|
||||
<div class="archive-year">
|
||||
<h2 th:text="${archive.year}"></h2>
|
||||
<th:block th:each="month : ${archive.months}">
|
||||
<h3 th:text="|${archive.year}-${month.month}|"></h3>
|
||||
<ul class="post-list">
|
||||
<li class="post-item" th:each="post : ${month.posts}">
|
||||
<h2 class="post-title">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</h2>
|
||||
<p class="post-meta">
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<nav class="pagination" th:if="${archives.hasPrevious() || archives.hasNext()}">
|
||||
<a th:if="${archives.hasPrevious()}" th:href="@{${archives.prevUrl}}">Previous</a>
|
||||
<span th:text="|${archives.page} / ${archives.totalPages}|"></span>
|
||||
<a th:if="${archives.hasNext()}" th:href="@{${archives.nextUrl}}">Next</a>
|
||||
</nav>
|
||||
</include>
|
||||
@@ -0,0 +1,16 @@
|
||||
<include src="layout.html">
|
||||
<template name="head">
|
||||
<title th:text="|${author.spec.displayName} - ${site.title}|">Author</title>
|
||||
</template>
|
||||
|
||||
<section>
|
||||
<h1 th:text="${author.spec.displayName}"></h1>
|
||||
<p th:if="${not #strings.isEmpty(author.spec.bio)}" th:text="${author.spec.bio}"></p>
|
||||
|
||||
<ul class="post-list">
|
||||
<include src="post-card.html"></include>
|
||||
</ul>
|
||||
|
||||
<include src="pagination.html"></include>
|
||||
</section>
|
||||
</include>
|
||||
@@ -0,0 +1,22 @@
|
||||
<include src="layout.html">
|
||||
<template name="head">
|
||||
<title th:text="|Categories - ${site.title}|">Categories</title>
|
||||
</template>
|
||||
|
||||
<h1>Categories</h1>
|
||||
<ul class="category-list">
|
||||
<li th:each="cat : ${categories.items}">
|
||||
<a
|
||||
th:href="@{${cat.status.permalink}}"
|
||||
th:text="|${cat.spec.displayName} (${cat.postCount})|"
|
||||
class="category"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<nav class="pagination" th:if="${categories.hasPrevious() || categories.hasNext()}">
|
||||
<a th:if="${categories.hasPrevious()}" th:href="@{${categories.prevUrl}}">Previous</a>
|
||||
<span th:text="|${categories.page} / ${categories.totalPages}|"></span>
|
||||
<a th:if="${categories.hasNext()}" th:href="@{${categories.nextUrl}}">Next</a>
|
||||
</nav>
|
||||
</include>
|
||||
@@ -0,0 +1,14 @@
|
||||
<include src="layout.html">
|
||||
<template name="head">
|
||||
<title th:text="|${category.spec.displayName} - ${site.title}|">Category Archive</title>
|
||||
</template>
|
||||
|
||||
<h1 th:text="|Category: ${category.spec.displayName}|"></h1>
|
||||
<p th:if="${category.spec.description}" th:text="${category.spec.description}"></p>
|
||||
|
||||
<ul class="post-list">
|
||||
<include src="post-card.html"></include>
|
||||
</ul>
|
||||
|
||||
<include src="pagination.html"></include>
|
||||
</include>
|
||||
@@ -0,0 +1,167 @@
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family:
|
||||
system-ui,
|
||||
-apple-system,
|
||||
sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0070f3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.site-header {
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.site-header .container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.site-title {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.site-nav ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.site-main {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.post-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.post-item {
|
||||
padding: 1.5rem 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.post-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.post-title {
|
||||
margin: 0 0 0.25rem;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
color: #666;
|
||||
font-size: 0.875rem;
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
.post-excerpt {
|
||||
color: #555;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 1rem;
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.article-header {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
color: #666;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.article-content {
|
||||
max-width: 100%;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.article-content img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.article-tags,
|
||||
.article-categories {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.tag,
|
||||
.category {
|
||||
display: inline-block;
|
||||
padding: 0.125rem 0.5rem;
|
||||
background: #f0f0f0;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875rem;
|
||||
margin: 0.25rem;
|
||||
}
|
||||
|
||||
.post-nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 2rem 0;
|
||||
border-top: 1px solid #eee;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
border-top: 1px solid #eee;
|
||||
padding: 1.5rem 0;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.archive-year {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.tag-list,
|
||||
.category-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<include src="layout.html">
|
||||
<template name="head">
|
||||
<title th:text="${site.title}">Home</title>
|
||||
<script type="module" src="./js/index.ts"></script>
|
||||
</template>
|
||||
|
||||
<ul class="post-list">
|
||||
<include src="post-card.html"></include>
|
||||
</ul>
|
||||
|
||||
<include src="pagination.html"></include>
|
||||
</include>
|
||||
@@ -0,0 +1 @@
|
||||
console.log("Hello, Halo Theme!");
|
||||
@@ -0,0 +1 @@
|
||||
import "../css/main.css";
|
||||
@@ -0,0 +1 @@
|
||||
console.log("Hello, Halo Theme Post!");
|
||||
@@ -0,0 +1,20 @@
|
||||
<include src="layout.html">
|
||||
<template name="head">
|
||||
<title th:text="|${singlePage.spec.title} - ${site.title}|">Page Title</title>
|
||||
</template>
|
||||
|
||||
<article>
|
||||
<header class="article-header">
|
||||
<h1 class="article-title" th:text="${singlePage.spec.title}"></h1>
|
||||
</header>
|
||||
<div class="article-content" th:utext="${singlePage.content.content}"></div>
|
||||
</article>
|
||||
|
||||
<div th:if="${haloCommentEnabled}">
|
||||
<halo:comment
|
||||
group="content.halo.run"
|
||||
kind="SinglePage"
|
||||
th:attr="name=${singlePage.metadata.name}"
|
||||
/>
|
||||
</div>
|
||||
</include>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<html lang="en" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<slot name="head">
|
||||
<title th:text="${site.title}">Site Title</title>
|
||||
</slot>
|
||||
<script type="module" src="./js/main.ts"></script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<div class="container">
|
||||
<a class="site-title" th:href="@{/}" th:text="${site.title}">Site Title</a>
|
||||
<nav class="site-nav" th:with="menu = ${menuFinder.getPrimary()}">
|
||||
<ul>
|
||||
<li th:each="item : ${menu.menuItems}">
|
||||
<a
|
||||
th:href="@{${item.status.href}}"
|
||||
th:text="${item.status.displayName}"
|
||||
th:target="${item.spec.target?.value}"
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="site-main">
|
||||
<div class="container">
|
||||
<slot />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p th:text="${theme.config.basic.custom_footer ?: site.title}"></p>
|
||||
<halo:footer />
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
<nav class="pagination" th:if="${posts.hasPrevious() || posts.hasNext()}">
|
||||
<a th:if="${posts.hasPrevious()}" th:href="@{${posts.prevUrl}}">Previous</a>
|
||||
<span th:text="|${posts.page} / ${posts.totalPages}|"></span>
|
||||
<a th:if="${posts.hasNext()}" th:href="@{${posts.nextUrl}}">Next</a>
|
||||
</nav>
|
||||
@@ -0,0 +1,18 @@
|
||||
<li class="post-item" th:each="post : ${posts.items}">
|
||||
<h2 class="post-title">
|
||||
<a th:href="@{${post.status.permalink}}" th:text="${post.spec.title}"></a>
|
||||
</h2>
|
||||
<p class="post-meta">
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
<span th:if="${not #lists.isEmpty(post.categories)}">
|
||||
·
|
||||
<a
|
||||
th:each="cat : ${post.categories}"
|
||||
th:href="@{${cat.status.permalink}}"
|
||||
th:text="${cat.spec.displayName}"
|
||||
class="category"
|
||||
></a>
|
||||
</span>
|
||||
</p>
|
||||
<p class="post-excerpt" th:text="${post.status.excerpt}"></p>
|
||||
</li>
|
||||
@@ -0,0 +1,56 @@
|
||||
<include src="layout.html">
|
||||
<template name="head">
|
||||
<title th:text="|${post.spec.title} - ${site.title}|">Post Title</title>
|
||||
<script type="module" src="./js/post.ts"></script>
|
||||
</template>
|
||||
|
||||
<article>
|
||||
<header class="article-header">
|
||||
<h1 class="article-title" th:text="${post.spec.title}"></h1>
|
||||
<div class="article-meta">
|
||||
<time th:text="${#temporals.format(post.spec.publishTime, 'yyyy-MM-dd')}"></time>
|
||||
<span th:if="${not #lists.isEmpty(post.categories)}">
|
||||
·
|
||||
<a
|
||||
th:each="cat : ${post.categories}"
|
||||
th:href="@{${cat.status.permalink}}"
|
||||
th:text="${cat.spec.displayName}"
|
||||
class="category"
|
||||
></a>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="article-content" th:utext="${post.content.content}"></div>
|
||||
|
||||
<div class="article-tags" th:if="${not #lists.isEmpty(post.tags)}">
|
||||
<a
|
||||
th:each="tag : ${post.tags}"
|
||||
th:href="@{${tag.status.permalink}}"
|
||||
th:text="${tag.spec.displayName}"
|
||||
class="tag"
|
||||
></a>
|
||||
</div>
|
||||
|
||||
<nav class="post-nav" th:with="cursor = ${postFinder.cursor(post.metadata.name)}">
|
||||
<span>
|
||||
<a
|
||||
th:if="${cursor.hasPrevious()}"
|
||||
th:href="@{${cursor.previous.status.permalink}}"
|
||||
th:text="|← ${cursor.previous.spec.title}|"
|
||||
></a>
|
||||
</span>
|
||||
<span>
|
||||
<a
|
||||
th:if="${cursor.hasNext()}"
|
||||
th:href="@{${cursor.next.status.permalink}}"
|
||||
th:text="|${cursor.next.spec.title} →|"
|
||||
></a>
|
||||
</span>
|
||||
</nav>
|
||||
</article>
|
||||
|
||||
<div th:if="${haloCommentEnabled}">
|
||||
<halo:comment group="content.halo.run" kind="Post" th:attr="name=${post.metadata.name}" />
|
||||
</div>
|
||||
</include>
|
||||
@@ -0,0 +1,13 @@
|
||||
<include src="layout.html">
|
||||
<template name="head">
|
||||
<title th:text="|${tag.spec.displayName} - ${site.title}|">Tag Archive</title>
|
||||
</template>
|
||||
|
||||
<h1 th:text="|Tag: ${tag.spec.displayName}|"></h1>
|
||||
|
||||
<ul class="post-list">
|
||||
<include src="post-card.html"></include>
|
||||
</ul>
|
||||
|
||||
<include src="pagination.html"></include>
|
||||
</include>
|
||||
@@ -0,0 +1,18 @@
|
||||
<include src="layout.html">
|
||||
<template name="head">
|
||||
<title th:text="|Tags - ${site.title}|">Tags</title>
|
||||
</template>
|
||||
|
||||
<h1>Tags</h1>
|
||||
<ul class="tag-list">
|
||||
<li th:each="tag : ${tags.items}">
|
||||
<a th:href="@{${tag.status.permalink}}" th:text="${tag.spec.displayName}" class="tag"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<nav class="pagination" th:if="${tags.hasPrevious() || tags.hasNext()}">
|
||||
<a th:if="${tags.hasPrevious()}" th:href="@{${tags.prevUrl}}">Previous</a>
|
||||
<span th:text="|${tags.page} / ${tags.totalPages}|"></span>
|
||||
<a th:if="${tags.hasNext()}" th:href="@{${tags.nextUrl}}">Next</a>
|
||||
</nav>
|
||||
</include>
|
||||
@@ -0,0 +1,20 @@
|
||||
apiVersion: theme.halo.run/v1alpha1
|
||||
kind: Theme
|
||||
metadata:
|
||||
name: theme-vite
|
||||
spec:
|
||||
displayName: Vite Theme
|
||||
author:
|
||||
name: Your Name
|
||||
website: https://example.com
|
||||
description: A Halo theme powered by vite-plugin-halo-theme
|
||||
logo: ""
|
||||
homepage: ""
|
||||
repo: ""
|
||||
issues: ""
|
||||
settingName: "theme-vite-setting"
|
||||
configMapName: "theme-vite-configmap"
|
||||
version: 1.0.0
|
||||
requires: ">=2.0.0"
|
||||
license:
|
||||
- name: "MIT"
|
||||
@@ -0,0 +1,6 @@
|
||||
import { defineConfig } from "vite";
|
||||
import { haloThemePlugin } from "@halo-dev/vite-plugin-halo-theme";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [haloThemePlugin()],
|
||||
});
|
||||
Reference in New Issue
Block a user