fix maybe

This commit is contained in:
MacrosMeng 2026-05-16 04:24:10 +08:00
parent 8d5f5f0e30
commit 928d71db75
90 changed files with 8326 additions and 0 deletions

22
source/_data/about.yml Normal file
View File

@ -0,0 +1,22 @@
name: "OhMyIWB"
title: "不定期分享 IWB 小知识、软件、更新等内容"
description: "你好👋这里是OMI"
skills:
- "Updates"
- "Skills"
- "Softwares"
- "More"
contacts:
- name: "STCN论坛帖子"
icon: "chat"
link: "https://forum.smart-teach.cn/d/1783"
- name: "联系我"
icon: "email"
link: "mailto:meng-yc@outook.com"
projects:
- name: "1145141919810"
description: "我是滚木"
link: "about:blank"

9
source/_data/links.yml Normal file
View File

@ -0,0 +1,9 @@
- name: STCN 论坛
url: https://forum.smart-teach.cn
avatar: https://forum.smart-teach.cn/assets/favicon-v4ksoaxf.png
desc: 智教联盟论坛 - 智教联盟致力于为教学辅助类应用提供全方面扶持与帮助。
- name: MacrosMeng
url: https://mm666.qzz.io
avatar: https://mm666.qzz.io/images/AvatarPic.png
desc: MacrosMeng 的个人网站

View File

@ -0,0 +1,127 @@
name: Hexo Theme Release
on:
push:
branches: [ main ]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
registry-url: 'https://registry.npmjs.org'
- name: Install dependencies
run: |
npm install --ignore-scripts
- name: Smart version management and publish
run: |
COMMIT_MSG=$(git log -1 --pretty=%B)
echo "Commit: $COMMIT_MSG"
# 1. 检查包是否存在并获取最新版本
if npm view hexo-theme-materialis version >/dev/null 2>&1; then
LATEST_NPM_VERSION=$(npm view hexo-theme-materialis version)
echo "📦 Latest version on npm: $LATEST_NPM_VERSION"
PACKAGE_EXISTS=true
else
echo "🆕 Package not found on npm (first publish or access issue)"
LATEST_NPM_VERSION="0.0.0"
PACKAGE_EXISTS=false
fi
CURRENT_VERSION=$(node -p "require('./package.json').version")
echo "📁 Current version in repo: $CURRENT_VERSION"
# 2. 确定目标版本逻辑
if [ "$PACKAGE_EXISTS" = false ]; then
# 首次发布:直接使用仓库版本
echo "🚀 First publish scenario! Using repo version: $CURRENT_VERSION"
TARGET_VERSION="$CURRENT_VERSION"
elif [ "$(printf '%s\n' "$LATEST_NPM_VERSION" "$CURRENT_VERSION" | sort -V | tail -n1)" = "$CURRENT_VERSION" ] && [ "$CURRENT_VERSION" != "$LATEST_NPM_VERSION" ]; then
# 仓库版本较新:使用仓库版本
echo "✅ Using newer repo version: $CURRENT_VERSION"
TARGET_VERSION="$CURRENT_VERSION"
else
# 基于Commit Message计算新版本
if [[ "$COMMIT_MSG" =~ .*BREAKING\ CHANGE.* ]] || \
[[ "$COMMIT_MSG" =~ .*BREAKING\ CHANGES.* ]] || \
[[ "$COMMIT_MSG" =~ ^feat! ]] || \
[[ "$COMMIT_MSG" =~ ^refactor! ]] || \
[[ "$COMMIT_MSG" =~ ^(major|release|version)[[:space:]]+[0-9] ]] || \
[[ "$COMMIT_MSG" =~ .*不兼容.* ]] || \
[[ "$COMMIT_MSG" =~ .*重大更新.* ]] || \
[[ "$COMMIT_MSG" =~ .*重大变更.* ]]; then
BUMP="major"
echo "🎯 Bump type: major (breaking change detected)"
elif [[ "$COMMIT_MSG" =~ ^feat: ]]; then
BUMP="minor"
echo "✨ Bump type: minor (feature)"
else
BUMP="patch"
echo "🔧 Bump type: patch (fix/other)"
fi
# 基于线上真实版本号计算
MAJOR=$(echo $LATEST_NPM_VERSION | cut -d. -f1)
MINOR=$(echo $LATEST_NPM_VERSION | cut -d. -f2)
PATCH=$(echo $LATEST_NPM_VERSION | cut -d. -f3)
case "$BUMP" in
"major")
TARGET_VERSION="$((MAJOR + 1)).0.0"
echo "🚀 MAJOR version update: $LATEST_NPM_VERSION -> $TARGET_VERSION"
;;
"minor")
TARGET_VERSION="$MAJOR.$((MINOR + 1)).0"
echo "🔄 MINOR version update: $LATEST_NPM_VERSION -> $TARGET_VERSION"
;;
"patch")
TARGET_VERSION="$MAJOR.$MINOR.$((PATCH + 1))"
echo "🔧 PATCH version update: $LATEST_NPM_VERSION -> $TARGET_VERSION"
;;
esac
fi
echo "🎯 Target version: $TARGET_VERSION"
# 3. 更新本地package.json版本仅当版本变化时
if [ "$CURRENT_VERSION" != "$TARGET_VERSION" ]; then
echo "📝 Updating package.json version from $CURRENT_VERSION to $TARGET_VERSION..."
npm version $TARGET_VERSION --no-git-tag-version --allow-same-version --ignore-scripts
echo "✅ package.json updated"
else
echo "📝 Version unchanged, no need to update package.json"
fi
# 4. 发布到npm带更详细的错误处理
echo "🔍 Checking if version $TARGET_VERSION already exists..."
if npm view hexo-theme-materialis@$TARGET_VERSION version >/dev/null 2>&1; then
echo "✅ Version $TARGET_VERSION already published, skipping publish"
else
echo "🚀 Publishing version $TARGET_VERSION to npm..."
if npm publish; then
echo "✅ Successfully published hexo-theme-materialis@$TARGET_VERSION to npm!"
else
echo "❌ npm publish failed with exit code $?"
# 检查可能的具体错误
if [ $? -eq 128 ] || [ $? -eq 1 ]; then
echo "⚠️ This might be a permission issue. Please verify:"
echo " 1. Your NPM_TOKEN has write access to this package"
echo " 2. Package 'hexo-theme-materialis' actually exists and you're a maintainer"
fi
exit 1
fi
fi
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

139
themes/materialis/.gitignore vendored Normal file
View File

@ -0,0 +1,139 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.*
!.env.example
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Sveltekit cache directory
.svelte-kit/
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# Firebase cache directory
.firebase/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v3
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Vite logs files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

View File

@ -0,0 +1,41 @@
## Commit Message 规范
```
<type>:<title>
<body>
```
title 以动词开头,尽量少使用标点符号,简洁精炼,能省略掉的应省略
### Type
feat用于新增功能
fix用于修复问题
build: 用于修改项目构建系统,例如修改依赖库、外部接口或者升级 Node 版本等;
chore: 用于对非业务性代码进行修改,例如修改构建流程或者工具配置等;
ci: 用于修改持续集成流程,例如修改 Travis、Jenkins 等工作流配置;
docs: 用于修改文档,例如修改 README 文件、API 文档等;
style: 用于修改代码的样式,例如调整缩进、空格、空行等;
refactor: 用于重构代码,例如修改代码结构、变量名、函数名等但不修改功能逻辑;
perf: 用于优化性能,例如提升代码的性能、减少内存占用等;
test: 用于修改测试用例,例如添加、删除、修改代码的测试用例等。
### Body
请分点书写,格式如下:
```
- xxx
- xxx
- xxx
```

661
themes/materialis/LICENSE Normal file
View File

@ -0,0 +1,661 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.

View File

@ -0,0 +1,34 @@
# Hexo-Theme-Materialis
![GitHub stars](https://img.shields.io/github/stars/Can1425/Hexo-Theme-Materialis?style=social)
![GitHub forks](https://img.shields.io/github/forks/Can1425/Hexo-Theme-Materialis?style=social)
![npm](https://img.shields.io/npm/v/hexo-theme-materialis)
![GitHub issues](https://img.shields.io/github/issues/Can1425/Hexo-Theme-Materialis)
![GitHub last commit](https://img.shields.io/github/last-commit/Can1425/Hexo-Theme-Materialis)
![GitHub contributors](https://img.shields.io/github/contributors/Can1425/Hexo-Theme-Materialis)
![GitHub repo size](https://img.shields.io/github/repo-size/Can1425/Hexo-Theme-Materialis)
![GitHub license](https://img.shields.io/github/license/Can1425/Hexo-Theme-Materialis)
# 这是什么?
这是一个基于Hexo的博客主题采用Material Design风格当然也融合了一部分md3e的设计基于MDUI框架同时也支持响应式设计在移动端也有很好的显示效果。
# 特性
- 基于Material Design 3 设计风格
- 注释很多
- 适配多种语言
- 响应式设计,适配移动端
# 关于本项目
## 作者
本项目目前仅有两位作者当然如果你愿意你也可以共同维护此项目或者给本项目提issue或者给本项目提PR我们会非常欢迎你的贡献。
- [Can1425](https://github.com/Can1425)
- [system:error](https://github.com/systemerror111)
## 许可证
本项目采用 AGPL-3.0 license 许可证,你可以在遵守许可证的前提下自由使用、修改和分发本项目的代码。
## 后记
本主题是一个不完美的作品它有很多问题例如大部分ejs文件都没有外联因为最开始的时候偷懒比如可能有奇奇怪怪的bug但是由于两位作者均为初三的学生因此没有精力去让他变得完美所以如果你有时间的话或许可以提一提issue或者可以发PR我们非常欢迎本主题目前还没有设置文档或许会用vuepress但是目前会先把markdown文件放在/docs目录下欢迎查看
# 鸣谢
[MDUI](https://github.com/zdhxiong/mdui)
[Hexo](https://hexo.io/zh-cn/)
[Material Design](https://material.io/design)
[fancybox](https://fancyapps.com/fancybox/)
# 获得帮助
其实我认为yml文件中注释已经很清楚了
你可以自行查阅/docs下的markdown文件如果是hexo问题请查阅[hexo文档](https://hexo.io/zh-cn/docs/)如果仍无法解决可以加入QQ群928669012
注:提问时请注意言辞,如果可以,阅读[《提问的智慧》](https://lug.ustc.edu.cn/wiki/doc/smart-questions/),这会帮助你更好地描述问题,也会更快速地得到帮助。

View File

@ -0,0 +1,159 @@
#欢迎使用 Materialis 主题
#主题作者W-Can1425 system:error
#Comment 请选择一个
#---start---
comment:
service: twikoo # 可选: twikoo, waline, giscus
# 侧边栏配置
#---start---
sidebar:
# 用户信息
name: "system:error"
slogen: "不定期分享 IWB 小知识、软件、更新等内容"
avatar: "https://github.com/MacroMeng.png"
# 导航菜单
nav:
- name: "首页"
icon: "home"
url: "/"
- name: "文章归档"
icon: "article"
url: "/diary/"
- name: "分类"
icon: "category"
url: "/categories/"
- name: "标签"
icon: "tag"
url: "/tags/"
- name: "关于"
icon: "person"
url: "/about/"
- name: "友链"
icon: "link"
url: "/links/"
#---end---
#友链配置
#---start---
links:
use: 2 # 0关闭友链 1使用qexo友链 2使用主题友链
qexo:
url: https://qexo.blog.yizhixiaozhu.top #填写你qexo的地址
#---end---
#自定义代码
#---start---
# 自定义资源文件配置
custom:
# 自定义 CSS 文件
# css:
# - /css/custom.css
# - /css/user-style.css
# - https://cdn.example.com/external.css
# 自定义 JavaScript 文件
# js:
# - /js/custom.js
# - /js/user-script.js
# - https://cdn.example.com/external.js
# 自定义头部代码
# head: |
# <!-- 自定义 meta 标签或其他 head 内容 -->
# <meta name="custom-meta" content="value">
# 自定义底部代码
# footer: |
# <!-- 自定义统计代码等 -->
# <script>
# console.log('自定义脚本');
# </script>
#---end---
#字体设置
#---start---
font:
primary: pingfang # 只能选择一个: lxwk dingtalk pingfang 或者false
#---end---
#Footer
#---start---
# Footer 配置
footer:
# 版权信息
copyright: "以 CC-BY-NC-SA 4.0 开放在公开领域"
# 备案信息(中国用户)
beian:
icp: ""
ps: ""
# 主题名称
theme_name: "materialis"
# 自定义徽章
# badges:
# - text: "开源"
# variant: "small"
# icon: "code"
# href: "https://github.com/yourname/your-theme"
# - text: "v1.0.0"
# variant: "small"
# icon: "tag"
# - text: "在线"
# variant: "small"
# icon: "wifi"
#---end---
#说说
#---start---
shuoshuo:
use: 0 # 0关闭说说 1使用qexo说说 2使用主题说说
qexo:
url: https://qexo.blog.yizhixiaozhu.top #填写你qexo的地址
banner:
title: "OhMyIWB"
subtitle: "不定期分享 IWB 小知识、软件、更新等内容"
projects:
enable: false
items:
- name: "项目一"
desc: "项目一的描述"
icon: "code"
link: "https://github.com/yourusername/project1"
color: "primary" # primary, secondary, tertiary, error, info
- name: "项目二"
desc: "项目二的描述"
icon: "terminal"
link: "https://github.com/yourusername/project2"
color: "secondary"
#---end---
#纪念日设置
#---start---
memorial_days:
enable: true # 是否启用纪念日黑白效果
dates: # 纪念日日期列表 (月-日格式)
- 7.7
- 9.18
- 12.13
#---end---
#链接拦截器
#---start---
interceptor:
enable: flase # 是否启用链接拦截器功能
#---end---
index_generator:
path: ''
per_page: 10
order_by: -date
#date按文章发布日期升序排列从旧到新
#-date按文章发布日期降序排列从新到旧- 最常用
#updated按文章更新日期升序排列
#-updated按文章更新日期降序排列
#title按文章标题字母顺序排列
#-title按文章标题字母倒序排列
#random随机排列

View File

@ -0,0 +1,15 @@
# 评论系统
评论系统配置项依赖于主题配置文件,因此需要您填写。
本主题支持以下评论系统:
- Discus
- Twikoo
- Waline
您可以自行添加其他评论系统,在高级教程里会讲到。
# Giscus
giscus 加载时,会使用 GitHub Discussions 搜索 API 根据选定的映射方式(如 URL、pathname、<title> 等)来查找与当前页面关联的 discussion。如果找不到匹配的 discussiongiscus bot 就会在第一次有人留下评论或回应时自动创建一个 discussion。
[Giscus文档](https://giscus.app/zh-CN)
# Twikoo
[Twikoo文档](https://twikoo.js.org/)
# Waline
[Waline文档](https://waline.js.org/)
根据文档中的要求填写对应项

View File

@ -0,0 +1,69 @@
# 配置
我在以下内容中将Hexo配置文件_config.yml称为框架配置将主题配置文件_config.materialis.yml称为主题配置文件。这二者有很大区别请您区分。
请将本主题的配置文件移动到Hexo根目录下并命名为_config.materialis.yml
# 站点信息配置
本主题的标题,语言等配置项依赖于框架配置文件,因此需要您填写。
```yml
title: Hexo
subtitle: ''
description: ''
keywords:
author: John Doe
language: zh-CN
timezone: ''
```
在第一项中,为您的站点标题,第二项为副标题,第三项为站点描述,第四项为关键词,第五项为作者名称,第六项为语言,第七项为时区。
## 语言
语言项填写'zh-CN',即可设置为中文简体。
语言项填写'zh-TW',即可设置为中文繁体。
语言项填写'en',即可设置为英文。
语言项填写'ja',即可设置为日语。
语言项填写'ko',即可设置为韩语。
语言项填写'fr',即可设置为法语。
语言项填写'it',即可设置为意大利语。
本主题暂时仅支持以上语言。
# 站点配置
## 侧边栏
```yml
sidebar:
# 用户信息
name: "system:error"
slogen: "长风破浪会有时,直挂云帆济沧海。"
avatar: "https://blog.yizhixiaozhu.top/images/1.png"
```
此项为配置侧边栏上的用户信息,头像配置请在 source/images 文件夹内放置一张图片,并填写图片名。
```yml
nav:
- name: "首页"
icon: "home"
url: "/"
- name: "文章归档"
icon: "article"
url: "/diary/"
- name: "分类"
icon: "category"
url: "/categories/"
- name: "标签"
icon: "tag"
url: "/tags/"
- name: "关于"
icon: "person"
url: "/about/"
- name: "友链"
icon: "link"
url: "/links/"
- name: "说说"
icon: "chat"
url: "/shuoshuo/"
```
配置项说明:
- name导航项名称
- icon导航项图标参考MDUI图标库
- url导航项链接

View File

@ -0,0 +1,26 @@
# Hexo 安装
本主题需要 Hexo 环境支持,若你还没有安装 Hexo请先参考 [Hexo 官方文档](https://hexo.io/zh-cn/docs/) 进行安装。
# 主题安装
你有两种安装方式:
## 方式一:通过 Git 安装
在你的Hexo博客根目录下执行以下命令
``` bash
cd themes
git clone https://github.com/Can1425/Hexo-Theme-Materialis.git
```
## 方式二:通过 npm 安装
在你的Hexo根目录下执行以下命令
``` bash
npm install hexo-theme-materialis
```
npm 安装后将不会在themes目录下生成请在node_modules目录下找到主题文件。复制到themes目录下
## 主题启用
在_config.yml文件中修改theme: materialis
现在在你的Hexo根目录下执行
``` bash
hexo clean
hexo generate
hexo server
```
查看你的博客是不是已经启用了Materialis主题。
恭喜你成功安装了本主题!

View File

@ -0,0 +1,130 @@
## About 页面用法
终端执行hexo new page "about"
创建成功后在source目录下生成about.md文件打开文件将内容替换为如下内容
```markdown
---
title: about
date: 2025-10-04 09:25:56
type: about
layout: about
---
```
然后新建source/_posts/about.yml文件将内容替换为如下内容
```yml
name: "你的名字"
title: "前端开发者 & 博主"
description: "这里写关于我的详细介绍,可以写多行文字..."
skills:
- "JavaScript"
- "HTML/CSS"
- "Vue.js"
- "React"
- "Node.js"
- "Python"
contacts:
- name: "GitHub"
icon: "code"
link: "https://github.com/yourname"
- name: "邮箱"
icon: "email"
link: "mailto:your@email.com"
- name: "微博"
icon: "chat"
link: "https://weibo.com/yourname"
projects:
- name: "个人博客系统"
description: "基于 Hexo 搭建的个人博客,支持自定义主题和插件"
link: "https://github.com/yourname/blog"
- name: "在线工具集合"
description: "开发的各种实用在线工具,包括图片处理、代码格式化等"
link: "https://tools.yoursite.com"
```
## 友情链接配置
新建页面links
在links里面替换掉内容
```markdown
---
title: links
date: 2025-10-04 18:47:34
type: friend
layout: friend
---
```
然后在创建source/_posts/links.yml文件将内容替换为如下内容
```yml
- name: 博客名称
url: https://example.com
avatar: https://example.com/avatar.jpg
desc: 博客描述
- name: 另一个博客
url: https://another-example.com
avatar: https://another-example.com/avatar.jpg
desc: 另一个博客的描述
```
## 归档页面
创建页面diary
在diary里面替换掉内容
```markdown
---
title: diary
date: 2025-10-04 19:04:54
type: diary
layout: diary
---
```
## 分类页面
创建页面categories
在categories里面替换掉内容
```markdown
---
title: categories
date: 2025-10-04 19:04:54
type: categories
layout: categories
---
```
## 标签页面
创建页面tags
在tags里面替换掉内容
```markdown
---
title: tags
date: 2025-10-04 19:04:54
type: tags
layout: tags
---
```
## 说说页面
创建页面shuoshuo
在shuoshuo里面替换掉内容
```markdown
---
title: shuoshuo
date: 2025-10-04 19:04:54
type: shuoshuo
layout: shuoshuo
---
## 添加说说
## 添加说说
1. 创建一个md文件在source/_data/文件名格式为talks.yml
2. 将内容替换为如下内容:
```yml
# 说说数据配置
talks:
- date: "2024-01-15 14:30"
author: "测试"
avatar: "/images/avatar.jpg" # 可选,不填使用默认头像
content: "测试"
# 配置说明
# date: 发布时间 (格式: YYYY-MM-DD HH:mm)
# author: 作者 (可选,默认使用 config.author)
# avatar: 头像链接 (可选)
# content: 说说内容
# images: 图片列表 (可选)
```

View File

@ -0,0 +1,15 @@
# 关于Qexo
Qexo是一个Hexo的"后端"为Hexo提供在线发布文章说说友链等诸如此类的功能。本主题深度适配了Qexo您可以选择使用本主题提供的功能也可以选择Qexo提供的。
# Qexo安装
Qexo安装请参考[Qexo安装](https://oplog.cn/qexo/start.html)
# Qexo配置
本主题适配Qexo的页面有友链说说页面
```yml
shuoshuo:
use: 1
qexo:
url:
```
在这里use 0 代表关闭说说1代表使用Qexo2代表使用本主题提供的说说功能
填写Qexo的urlhttps://qexo.example.com
links页面同理这里不再赘述

View File

@ -0,0 +1,12 @@
'use strict';
console.log('Hexo Theme Materialis loaded');
hexo.on('ready', () => {
hexo.log.info('Materialis theme initialized');
});
module.exports = {};

View File

@ -0,0 +1,105 @@
common:
home: Home
archive: Archive
category: Category
tag: Tag
about: About
links: Friends
shuoshuo: Moments
project: Project
search:
placeholder: Search posts, tags, categories...
start_typing: Type keywords to start searching
close: Close
load_failed_console: Failed to load search data
loading: Loading search data...
no_results_prefix: No results for
no_results_suffix: ""
found_prefix: Found
found_suffix: results
no_title: Untitled
no_data: No data
more: More
prev: Previous
next: Next
submit: Submit
author: Author
date: Date
empty: No content
banner:
title: Materialis
subtitle: A static blog theme based on Hexo
projects: Projects
footer:
copyright: All rights reserved
beian: Registration information
notfound:
title: Page not found
home: Return to homepage
back: Go back
shuoshuo:
no_talks: No shuoshuo yet
no_talks_desc: Haven't published any shuoshuo yet
no_config: No shuoshuo configuration, please check documentation to add
friend_links:
title: Friend Links
count_prefix: Total
count_suffix: friend links
empty: No friend links
empty_desc: No friend links added yet, feel free to contact me to exchange links
no_config: No friend link configuration, please check documentation to add
appbar:
light_mode: Light mode
dark_mode: Dark mode
auto_mode: Follow system
theme_tooltip: Theme
tags:
title: Tags
count_prefix: Total
count_suffix: tags
empty: No tags
empty_desc: No tags created yet, you can add tags to articles when writing
post:
prev: Previous post
next: Next post
none: No more
comment: Comment
comment_disabled: Comment function not enabled
index:
no_articles: No articles
no_articles_desc: No articles published yet
diary:
title: Article Archive
total: Total
articles: articles
year: Year
no_articles: No articles
no_articles_desc: No articles published yet, start writing to create your first blog post
categories:
title: Article Categories
count_prefix: Total
count_suffix: categories
articles: articles
empty: No categories
empty_desc: No categories created yet, you can add categories to articles when writing
about:
skills: Skills
contacts: Contact
projects: Projects
project_detail: View details
share:
copy_success: Link copied successfully!
copy_fail: Copy failed, please copy manually

View File

@ -0,0 +1,105 @@
common:
home: Accueil
archive: Archives
category: Catégorie
tag: Étiquette
about: À propos
links: Amis
shuoshuo: Moments
project: Projet
search:
placeholder: Rechercher des articles, étiquettes, catégories...
start_typing: Tapez des mots-clés pour commencer la recherche
close: Fermer
load_failed_console: Échec du chargement des données de recherche
loading: Chargement des données de recherche...
no_results_prefix: Aucun résultat pour
no_results_suffix: ""
found_prefix: Trouvé
found_suffix: résultats
no_title: Sans titre
no_data: Aucune donnée
more: Plus
prev: Précédent
next: Suivant
submit: Soumettre
author: Auteur
date: Date
empty: Aucun contenu
banner:
title: Materialis
subtitle: Un thème de blog statique basé sur Hexo
projects: Projets
footer:
copyright: Tous droits réservés
beian: Informations d'enregistrement
notfound:
title: Page non trouvée
home: Retour à l'accueil
back: Retour
shuoshuo:
no_talks: Aucun moment pour l'instant
no_talks_desc: Aucun moment publié pour le moment
no_config: Aucune configuration de moments, veuillez consulter la documentation pour ajouter
friend_links:
title: Liens d'amis
count_prefix: Total
count_suffix: liens d'amis
empty: Aucun lien d'ami
empty_desc: Aucun lien d'ami ajouté pour l'instant, n'hésitez pas à me contacter pour échanger des liens
no_config: Aucune configuration de liens d'amis, veuillez consulter la documentation pour ajouter
appbar:
light_mode: Mode clair
dark_mode: Mode sombre
auto_mode: Suivre le système
theme_tooltip: Thème
tags:
title: Étiquettes
count_prefix: Total
count_suffix: étiquettes
empty: Aucune étiquette
empty_desc: Aucune étiquette créée pour l'instant, vous pouvez ajouter des étiquettes aux articles lors de l'écriture
post:
prev: Article précédent
next: Article suivant
none: Plus d'articles
comment: Commentaire
comment_disabled: Fonction de commentaire non activée
index:
no_articles: Aucun article
no_articles_desc: Aucun article publié pour l'instant
diary:
title: Archives des articles
total: Total
articles: articles
year: Année
no_articles: Aucun article
no_articles_desc: Aucun article publié pour l'instant, commencez à écrire pour créer votre premier article de blog
categories:
title: Catégories d'articles
count_prefix: Total
count_suffix: catégories
articles: articles
empty: Aucune catégorie
empty_desc: Aucune catégorie créée pour l'instant, vous pouvez ajouter des catégories aux articles lors de l'écriture
about:
skills: Compétences
contacts: Contact
projects: Projets
project_detail: Voir les détails
share:
copy_success: Lien copié avec succès !
copy_fail: Échec de la copie, veuillez copier manuellement

View File

@ -0,0 +1,105 @@
common:
home: Home
archive: Archivio
category: Categoria
tag: Tag
about: Informazioni
links: Link amici
shuoshuo: Momenti
project: Progetto
search:
placeholder: Cerca post, tag, categorie...
start_typing: Digita le parole chiave per iniziare la ricerca
close: Chiudi
load_failed_console: Caricamento dati di ricerca fallito
loading: Caricamento dati di ricerca...
no_results_prefix: Nessun risultato per
no_results_suffix: ""
found_prefix: Trovati
found_suffix: risultati
no_title: Senza titolo
no_data: Nessun dato
more: Altro
prev: Precedente
next: Successivo
submit: Invia
author: Autore
date: Data
empty: Nessun contenuto
banner:
title: Materialis
subtitle: Un tema per blog statico basato su Hexo
projects: Progetti
footer:
copyright: Tutti i diritti riservati
beian: Informazioni di registrazione
notfound:
title: Pagina non trovata
home: Torna alla home
back: Indietro
shuoshuo:
no_talks: Ancora nessun momento
no_talks_desc: Non hai ancora pubblicato alcun momento
no_config: Nessuna configurazione momenti, controlla la documentazione per aggiungerla
friend_links:
title: Link amici
count_prefix: Totale
count_suffix: link amici
empty: Ancora nessun link amico
empty_desc: Non hai ancora aggiunto alcun link amico, contattami per scambiare link
no_config: Nessuna configurazione link amici, controlla la documentazione per aggiungerla
appbar:
light_mode: Modalità chiara
dark_mode: Modalità scura
auto_mode: Segui il sistema
theme_tooltip: Tema
tags:
title: Tag
count_prefix: Totale
count_suffix: tag
empty: Ancora nessun tag
empty_desc: Non hai ancora creato alcun tag, puoi aggiungere tag agli articoli quando scrivi
post:
prev: Articolo precedente
next: Articolo successivo
none: Nessun altro
comment: Commento
comment_disabled: Funzione commenti non abilitata
index:
no_articles: Ancora nessun articolo
no_articles_desc: Non hai ancora pubblicato alcun articolo
diary:
title: Archivio articoli
total: Totale
articles: articoli
year: Anno
no_articles: Ancora nessun articolo
no_articles_desc: Non hai ancora pubblicato alcun articolo, inizia a scrivere per creare il tuo primo post del blog
categories:
title: Categorie articoli
count_prefix: Totale
count_suffix: categorie
articles: articoli
empty: Ancora nessuna categoria
empty_desc: Non hai ancora creato alcuna categoria, puoi aggiungere categorie agli articoli quando scrivi
about:
skills: Competenze
contacts: Contatti
projects: Progetti
project_detail: Vedi dettagli
share:
copy_success: Link copiato con successo!
copy_fail: Copia fallita, copia manualmente

View File

@ -0,0 +1,105 @@
common:
home: ホーム
archive: アーカイブ
category: カテゴリ
tag: タグ
about: 关于
links: 友達リンク
shuoshuo: つぶやき
project: プロジェクト
search:
placeholder: 投稿、タグ、カテゴリを検索...
start_typing: キーワードを入力して検索を開始
close: 閉じる
load_failed_console: 検索データの読み込みに失敗しました
loading: 検索データを読み込み中...
no_results_prefix: ""
no_results_suffix: の検索結果はありません
found_prefix: ""
found_suffix: 件見つかりました
no_title: タイトルなし
no_data: データがありません
more: もっと見る
prev: 前へ
next: 次へ
submit: 送信
author: 著者
date: 日付
empty: コンテンツがありません
banner:
title: Materialis
subtitle: Hexoベースの静的ブログテーマ
projects: プロジェクト
footer:
copyright: 全著作権所有
beian: 登録情報
notfound:
title: ページが見つかりません
home: ホームに戻る
back: 戻る
shuoshuo:
no_talks: つぶやきはまだありません
no_talks_desc: まだつぶやきを投稿していません
no_config: つぶやきの設定がありません。ドキュメントを確認して追加してください
friend_links:
title: 友達リンク
count_prefix: 合計
count_suffix: 件の友達リンク
empty: 友達リンクはまだありません
empty_desc: まだ友達リンクを追加していません。リンク交換についてお気軽にお問い合わせください
no_config: 友達リンクの設定がありません。ドキュメントを確認して追加してください
appbar:
light_mode: ライトモード
dark_mode: ダークモード
auto_mode: システムに従う
theme_tooltip: テーマ
tags:
title: タグ
count_prefix: 合計
count_suffix: 個のタグ
empty: タグはまだありません
empty_desc: まだタグを作成していません。記事を書くときにタグを追加できます
post:
prev: 前の投稿
next: 次の投稿
none: これ以上ありません
comment: コメント
comment_disabled: コメント機能は有効になっていません
index:
no_articles: 記事はまだありません
no_articles_desc: まだ記事を公開していません
diary:
title: 投稿アーカイブ
total: 合計
articles: 件の記事
year:
no_articles: 記事はまだありません
no_articles_desc: まだ記事を公開していません。最初のブログ投稿を作成しましょう
categories:
title: 投稿カテゴリ
count_prefix: 合計
count_suffix: 個のカテゴリ
articles: 件の記事
empty: カテゴリはまだありません
empty_desc: まだカテゴリを作成していません。記事を書くときにカテゴリを追加できます
about:
skills: スキル
contacts: 連絡先
projects: プロジェクト
project_detail: 詳細を見る
share:
copy_success: リンクをコピーしました!
copy_fail: コピーに失敗しました。手動でコピーしてください

View File

@ -0,0 +1,105 @@
common:
home:
archive: 아카이브
category: 카테고리
tag: 태그
about: 소개
links: 친구 링크
shuoshuo: 이야기
project: 프로젝트
search:
placeholder: 게시물, 태그, 카테고리 검색...
start_typing: 키워드를 입력하여 검색 시작
close: 닫기
load_failed_console: 검색 데이터 로드 실패
loading: 검색 데이터 로드 중...
no_results_prefix: ""
no_results_suffix: 에 대한 검색 결과가 없습니다
found_prefix: ""
found_suffix: 개의 결과를 찾았습니다
no_title: 제목 없음
no_data: 데이터 없음
more: 더보기
prev: 이전
next: 다음
submit: 제출
author: 작성자
date: 날짜
empty: 내용 없음
banner:
title: Materialis
subtitle: Hexo 기반 정적 블로그 테마
projects: 프로젝트
footer:
copyright: 모든 권리 보유
beian: 등록 정보
notfound:
title: 페이지를 찾을 수 없습니다
home: 홈으로 돌아가기
back: 뒤로 가기
shuoshuo:
no_talks: 아직 이야기가 없습니다
no_talks_desc: 아직 이야기를 발행하지 않았습니다
no_config: 이야기 설정이 없습니다. 문서를 확인하여 추가해 주세요
friend_links:
title: 친구 링크
count_prefix:
count_suffix: 개의 친구 링크
empty: 아직 친구 링크가 없습니다
empty_desc: 아직 친구 링크를 추가하지 않았습니다. 링크 교환을 위해 저에게 연락해 주세요
no_config: 친구 링크 설정이 없습니다. 문서를 확인하여 추가해 주세요
appbar:
light_mode: 라이트 모드
dark_mode: 다크 모드
auto_mode: 시스템 따르기
theme_tooltip: 테마
tags:
title: 태그
count_prefix:
count_suffix: 개의 태그
empty: 아직 태그가 없습니다
empty_desc: 아직 태그를 생성하지 않았습니다. 글을 쓸 때 게시물에 태그를 추가할 수 있습니다
post:
prev: 이전 게시물
next: 다음 게시물
none: 더 이상 없습니다
comment: 댓글
comment_disabled: 댓글 기능이 활성화되지 않았습니다
index:
no_articles: 아직 게시물이 없습니다
no_articles_desc: 아직 게시물을 발행하지 않았습니다
diary:
title: 게시물 아카이브
total:
articles: 개의 게시물
year:
no_articles: 아직 게시물이 없습니다
no_articles_desc: 아직 게시물을 발행하지 않았습니다. 첫 번째 블로그 게시물을 작성해 보세요
categories:
title: 게시물 카테고리
count_prefix:
count_suffix: 개의 카테고리
articles: 개의 게시물
empty: 아직 카테고리가 없습니다
empty_desc: 아직 카테고리를 생성하지 않았습니다. 글을 쓸 때 게시물에 카테고리를 추가할 수 있습니다
about:
skills: 기술
contacts: 연락처
projects: 프로젝트
project_detail: 자세히 보기
share:
copy_success: 링크가 복사되었습니다!
copy_fail: 복사에 실패했습니다. 수동으로 복사해 주세요

View File

@ -0,0 +1,106 @@
common:
home: 首页
archive: 文章归档
category: 分类
tag: 标签
about: 关于
links: 友链
shuoshuo: 说说
project: 项目
search:
placeholder: 搜索文章、标签、分类...
start_typing: 输入关键词开始搜索
close: 关闭
load_failed_console: 搜索数据加载失败
loading: 搜索数据加载中...
no_results_prefix: 没有找到包含
no_results_suffix: 的内容
found_prefix: 找到
found_suffix: 个结果
no_title: 无标题
no_data: 暂无数据
more: 更多
prev: 上一页
next: 下一页
submit: 提交
author: 作者
date: 日期
empty: 暂无内容
banner:
title: Materialis
subtitle: 一个基于Hexo的静态博客主题
projects: 项目
footer:
copyright: 保留所有权利
beian: 备案信息
notfound:
title: 页面未找到
home: 返回首页
back: 返回上页
shuoshuo:
no_talks: 暂无说说
no_talks_desc: 还没有发布任何说说
no_config: 暂无说说配置,请查看文档添加
friend_links:
title: 友情链接
count_prefix:
count_suffix: 个友链
empty: 暂无友链
empty_desc: 还没有添加任何友情链接,欢迎联系我交换友链
no_config: 暂无友链配置,请查看文档添加
appbar:
light_mode: 亮色模式
dark_mode: 暗色模式
auto_mode: 跟随系统
theme_tooltip: 主题
tags:
title: 标签
count_prefix:
count_suffix: 个标签
empty: 暂无标签
empty_desc: 还没有创建任何标签,开始写作时可以为文章添加标签
post:
prev: 上一篇
next: 下一篇
none: 没有了
comment: 评论
comment_disabled: 评论功能未启用
index:
no_articles: 暂无文章
no_articles_desc: 还没有发布任何文章
diary:
title: 文章归档
total:
articles: 篇文章
year:
no_articles: 暂无文章
no_articles_desc: 还没有发布任何文章,开始写作来创建你的第一篇博客吧
categories:
title: 文章分类
count_prefix:
count_suffix: 个分类
articles: 篇文章
empty: 暂无分类
empty_desc: 还没有创建任何分类,开始写作时可以为文章添加分类
about:
skills: 技能
contacts: 联系方式
projects: 项目
project_detail: 查看详情
share:
copy_success: 链接复制成功!
copy_fail: 复制失败,请手动复制

View File

@ -0,0 +1,105 @@
common:
home: 首頁
archive: 文章歸檔
category: 分類
tag: 標籤
about: 關於
links: 友鏈
shuoshuo: 說說
project: 項目
search:
placeholder: 搜尋文章、標籤、分類...
start_typing: 輸入關鍵詞開始搜尋
close: 關閉
load_failed_console: 搜尋數據加載失敗
loading: 搜尋數據加載中...
no_results_prefix: 沒有找到包含
no_results_suffix: 的內容
found_prefix: 找到
found_suffix: 個結果
no_title: 無標題
no_data: 暫無資料
more: 更多
prev: 上一頁
next: 下一頁
submit: 提交
author: 作者
date: 日期
empty: 暫無內容
notfound:
title: 頁面未找到
home: 返回首頁
back: 返回上頁
banner:
title: Materialis
subtitle: 一個基於Hexo的靜態部落格主題
projects: 項目
footer:
copyright: 保留所有權利
beian: 備案資訊
shuoshuo:
no_talks: 暫無說說
no_talks_desc: 還沒有發布任何說說
no_config: 暫無說說配置,請查看文檔添加
friend_links:
title: 友鏈
count_prefix:
count_suffix: 個友鏈
empty: 暫無友鏈
empty_desc: 還沒有添加任何友情鏈接,歡迎聯繫我交換友鏈
no_config: 暫無友鏈配置,請查看文檔添加
appbar:
light_mode: 亮色模式
dark_mode: 暗色模式
auto_mode: 跟隨系統
theme_tooltip: 主題
tags:
title: 標籤
count_prefix:
count_suffix: 個標籤
empty: 暫無標籤
empty_desc: 還沒有創建任何標籤,開始寫作時可以為文章添加標籤
post:
prev: 上一篇
next: 下一篇
none: 沒有了
comment: 評論
comment_disabled: 評論功能未啟用
index:
no_articles: 暫無文章
no_articles_desc: 還沒有發佈任何文章
diary:
title: 文章歸檔
total:
articles: 篇文章
year:
no_articles: 暫無文章
no_articles_desc: 還沒有發佈任何文章,開始寫作來創建你的第一篇博客吧
categories:
title: 文章分類
count_prefix:
count_suffix: 個分類
articles: 篇文章
empty: 暫無分類
empty_desc: 還沒有創建任何分類,開始寫作時可以為文章添加分類
about:
skills: 技能
contacts: 聯繫方式
projects: 項目
project_detail: 查看詳情
share:
copy_success: 連結複製成功!
copy_fail: 複製失敗,請手動複製

View File

@ -0,0 +1,25 @@
<div style="min-height: 80vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 24px; padding: 40px;">
<mdui-card class="mdui-primary" style="width: 100%; max-width: 400px; padding: 40px 32px;">
<div style="text-align: center;">
<mdui-typography variant="h2" style="margin-bottom: 16px;">404</mdui-typography>
<br>
<mdui-typography variant="h6" style="opacity: 0.8;"><%= __('notfound.title') %></mdui-typography>
</div>
</mdui-card>
<mdui-card class="mdui-primary" style="width: 100%; max-width: 400px; padding: 32px;">
<div style="display: flex; flex-direction: column; gap: 16px;">
<mdui-button variant="filled" href="<%= url_for('/') %>" full-width>
<mdui-icon slot="icon">home</mdui-icon>
<%= __('notfound.home') %>
</mdui-button>
<mdui-button variant="outlined" onclick="history.back()" full-width>
<mdui-icon slot="icon">arrow_back</mdui-icon>
<%= __('notfound.back') %>
</mdui-button>
</div>
</mdui-card>
</div>

View File

@ -0,0 +1,382 @@
<style>
.about-page {
max-width: 900px;
margin: 0 auto;
padding: 40px 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
.about-header {
width: 100%;
text-align: center;
padding: 40px 32px;
border-radius: 24px;
background-color: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
}
.about-avatar {
width: 120px;
height: 120px;
border-radius: 50%;
border: 4px solid rgba(255, 255, 255, 0.3);
margin: 0 auto 20px;
object-fit: cover;
}
.about-name {
font-size: 28px;
font-weight: 500;
margin-bottom: 8px;
}
.about-title {
font-size: 18px;
opacity: 0.9;
margin-bottom: 16px;
}
.about-description {
font-size: 16px;
line-height: 1.6;
opacity: 0.8;
max-width: 600px;
margin: 0 auto;
}
.about-section {
width: 100%;
border-radius: 20px;
overflow: hidden;
background-color: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface));
}
.about-section-header {
padding: 24px 32px;
background: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-primary-container));
}
.about-section-content {
padding: 32px;
}
.skills-container {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.skill-chip {
--mdui-chip-height: 36px;
--mdui-chip-container-color: var(--mdui-color-secondary-container);
--mdui-chip-label-text-color: var(--mdui-color-on-secondary-container);
border-radius: 18px;
}
.contacts-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
.contact-card {
display: flex;
align-items: center;
gap: 16px;
padding: 20px;
border-radius: 16px;
background-color: rgb(var(--mdui-color-surface-container-high));
text-decoration: none;
color: inherit;
transition: all 0.2s ease;
}
.contact-card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
background-color: rgb(var(--mdui-color-surface-container-highest));
}
.contact-icon {
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background-color: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
font-size: 24px;
}
.contact-content {
display: flex;
flex-direction: column;
flex: 1;
}
.contact-name {
font-size: 16px;
font-weight: 500;
margin-bottom: 4px;
}
.contact-link {
font-size: 14px;
opacity: 0.7;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.projects-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 24px;
}
.project-card {
border-radius: 16px;
overflow: hidden;
background-color: rgb(var(--mdui-color-surface-container-high));
transition: all 0.2s ease;
}
.project-card:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.1);
}
.project-content {
padding: 24px;
}
.project-name {
font-size: 18px;
font-weight: 500;
margin-bottom: 12px;
}
.project-description {
font-size: 14px;
line-height: 1.5;
opacity: 0.8;
margin-bottom: 20px;
}
.project-link {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 14px;
text-decoration: none;
color: rgb(var(--mdui-color-primary));
font-weight: 500;
}
.project-link:hover {
text-decoration: underline;
}
/* 平板设备 */
@media (max-width: 1024px) {
.about-page {
padding: 32px 20px;
gap: 20px;
}
.about-header {
padding: 32px 24px;
}
.about-section-content {
padding: 24px;
}
.contacts-grid {
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 16px;
}
.projects-grid {
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
}
}
/* 手机设备 */
@media (max-width: 768px) {
.about-page {
padding: 24px 16px;
gap: 16px;
}
.about-header {
padding: 24px 20px;
}
.about-avatar {
width: 100px;
height: 100px;
}
.about-name {
font-size: 24px;
}
.about-title {
font-size: 16px;
}
.about-section-content {
padding: 20px;
}
.contacts-grid {
grid-template-columns: 1fr;
gap: 12px;
}
.contact-card {
padding: 16px;
}
.projects-grid {
grid-template-columns: 1fr;
gap: 16px;
}
.project-content {
padding: 20px;
}
}
/* 小屏手机 */
@media (max-width: 480px) {
.about-page {
padding: 20px 12px;
gap: 12px;
}
.about-header {
padding: 20px 16px;
}
.about-avatar {
width: 90px;
height: 90px;
}
.about-name {
font-size: 22px;
}
.about-description {
font-size: 14px;
}
.about-section-content {
padding: 16px;
}
.contact-icon {
width: 45px;
height: 45px;
font-size: 20px;
}
.contact-name {
font-size: 15px;
}
.project-name {
font-size: 16px;
}
}
</style>
<div class="about-page">
<!-- 页面标题 -->
<mdui-card class="about-header">
<% if (theme.sidebar.avatar) { %>
<img src="<%= theme.sidebar.avatar %>" alt="Avatar" class="about-avatar">
<% } %>
<div class="about-name"><%= theme.sidebar.name || theme.data.about.name %></div>
<div class="about-title"><%= theme.data.about.title %></div>
<div class="about-description"><%= theme.data.about.description %></div>
</mdui-card>
<!-- 技能部分 -->
<% if (site.data.about.skills && site.data.about.skills.length > 0) { %>
<mdui-card class="about-section">
<div class="about-section-header">
<mdui-typography variant="h5" style="font-weight: 500;"><%= __('about.skills') %></mdui-typography>
</div>
<div class="about-section-content">
<div class="skills-container">
<% site.data.about.skills.forEach(function(skill) { %>
<mdui-chip class="skill-chip" variant="filled">
<%= skill %>
</mdui-chip>
<% }); %>
</div>
</div>
</mdui-card>
<% } %>
<!-- 联系方式 -->
<% if (site.data.about.contacts && site.data.about.contacts.length > 0) { %>
<mdui-card class="about-section">
<div class="about-section-header">
<mdui-typography variant="h5" style="font-weight: 500;"><%= __('about.contacts') %></mdui-typography>
</div>
<div class="about-section-content">
<div class="contacts-grid">
<% site.data.about.contacts.forEach(function(contact) { %>
<a href="<%= contact.link %>" class="contact-card" target="_blank" rel="noopener">
<div class="contact-icon">
<mdui-icon><%= contact.icon || 'link' %></mdui-icon>
</div>
<div class="contact-content">
<div class="contact-name"><%= contact.name %></div>
<div class="contact-link"><%= contact.link.replace('mailto:', '').replace('https://', '').replace('http://', '') %></div>
</div>
</a>
<% }); %>
</div>
</div>
</mdui-card>
<% } %>
<!-- 项目展示 -->
<% if (site.data.about.projects && site.data.about.projects.length > 0) { %>
<mdui-card class="about-section">
<div class="about-section-header">
<mdui-typography variant="h5" style="font-weight: 500;"><%= __('about.projects') %></mdui-typography>
</div>
<div class="about-section-content">
<div class="projects-grid">
<% site.data.about.projects.forEach(function(project) { %>
<div class="project-card">
<div class="project-content">
<div class="project-name"><%= project.name %></div>
<div class="project-description"><%= project.description %></div>
<% if (project.link) { %>
<a href="<%= project.link %>" class="project-link" target="_blank" rel="noopener">
<%= __('about.project_detail') %>
<mdui-icon>arrow_forward</mdui-icon>
</a>
<% } %>
</div>
</div>
<% }); %>
</div>
</div>
</mdui-card>
<% } %>
</div>

View File

@ -0,0 +1,277 @@
<style>
.categories-page {
max-width: 900px;
margin: 0 auto;
padding: 40px 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
.categories-header {
width: 100%;
text-align: center;
padding: 40px 32px;
border-radius: 24px;
background-color: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
}
.categories-list {
display: flex;
flex-direction: column;
width: 100%;
gap: 16px;
}
.category-item {
display: flex;
align-items: center;
gap: 16px;
padding: 20px 32px;
text-decoration: none;
color: inherit;
border-radius: 16px;
background-color: rgb(var(--mdui-color-surface-container));
transition: all 0.2s ease;
border: 1px solid rgba(var(--mdui-color-outline), 0.2);
}
.category-item:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
background-color: rgb(var(--mdui-color-surface-container-high));
}
.category-icon {
flex-shrink: 0;
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background-color: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
font-size: 24px;
}
.category-content {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
}
.category-name {
font-size: 18px;
font-weight: 500;
margin-bottom: 6px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.category-description {
font-size: 14px;
opacity: 0.7;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
}
.category-meta {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
opacity: 0.7;
}
.category-arrow {
flex-shrink: 0;
color: rgb(var(--mdui-color-outline));
transition: transform 0.2s ease;
}
.category-item:hover .category-arrow {
transform: translateX(4px);
}
.categories-empty-state {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin-top: 40px;
}
.categories-empty-card {
width: 100%;
max-width: 500px;
padding: 60px 40px;
text-align: center;
border-radius: 24px;
display: flex;
flex-direction: column;
align-items: center;
background-color: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface));
}
.categories-empty-icon {
font-size: 80px;
margin-bottom: 24px;
opacity: 0.6;
color: rgb(var(--mdui-color-primary-container));
}
.categories-empty-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
/* 平板设备 */
@media (max-width: 1024px) {
.categories-page {
padding: 32px 20px;
gap: 20px;
}
.categories-header {
padding: 32px 24px;
}
.category-item {
padding: 18px 24px;
}
}
/* 手机设备 */
@media (max-width: 768px) {
.categories-page {
padding: 24px 16px;
gap: 16px;
}
.categories-header {
padding: 24px 20px;
}
.category-item {
padding: 16px 20px;
gap: 12px;
}
.category-icon {
width: 45px;
height: 45px;
font-size: 20px;
}
.categories-empty-card {
padding: 40px 24px;
}
.categories-empty-icon {
font-size: 60px;
margin-bottom: 20px;
}
}
/* 小屏手机 */
@media (max-width: 480px) {
.categories-page {
padding: 20px 12px;
gap: 12px;
}
.categories-header {
padding: 20px 16px;
}
.category-item {
padding: 14px 16px;
gap: 10px;
}
.category-icon {
width: 40px;
height: 40px;
font-size: 18px;
}
.category-name {
font-size: 16px;
}
.category-description {
font-size: 13px;
}
.category-meta {
font-size: 12px;
}
}
</style>
<div class="categories-page">
<!-- 页面标题 -->
<mdui-card class="categories-header">
<mdui-typography variant="h3" style="margin-bottom: 12px; font-weight: 500;"><%= __('categories.title') %></mdui-typography>
<mdui-typography variant="h6" style="opacity: 0.8; font-weight: 400;">
<%= __('categories.count_prefix') %> <mdui-badge style="margin: 0 8px;"><%= site.categories.length %></mdui-badge> <%= __('categories.count_suffix') %>
</mdui-typography>
</mdui-card>
<!-- 分类列表 -->
<% if (site.categories.length > 0) { %>
<div class="categories-list">
<% site.categories.sort('name').each(function(category) { %>
<a href="<%= url_for(category.path) %>" class="category-item">
<div class="category-icon">
<mdui-icon>folder</mdui-icon>
</div>
<div class="category-content">
<div class="category-name">
<%= category.name %>
</div>
<% if (category.description) { %>
<div class="category-description">
<%= category.description %>
</div>
<% } %>
<div class="category-meta">
<span><%= category.posts.length %> <%= __('categories.articles') %></span>
</div>
</div>
<mdui-icon class="category-arrow">chevron_right</mdui-icon>
</a>
<% }); %>
</div>
<% } %>
<!-- 空状态 -->
<% if (!site.categories || site.categories.length === 0) { %>
<div class="categories-empty-state">
<mdui-card class="categories-empty-card">
<mdui-icon class="categories-empty-icon">category</mdui-icon>
<div class="categories-empty-content">
<mdui-typography variant="h5" style="margin-bottom: 8px; font-weight: 500;">
<%= __('categories.empty') %>
</mdui-typography>
<mdui-typography variant="body" style="opacity: 0.7; line-height: 1.5;">
<%= __('categories.empty_desc') %>
</mdui-typography>
</div>
</mdui-card>
</div>
<% } %>
</div>

View File

@ -0,0 +1,391 @@
<style>
.post-card-title {
background-color: rgb(var(--mdui-color-surface-container-low));
}
.post-card-title-text {
color: rgb(var(--mdui-color-on-surface-container-low));
}
.article-title {
color: rgb(var(--mdui-color-primary));
}
.article-card {
background-color: rgb(var(--mdui-color-surface-container-low));
}
.banner-title {
background-color: rgb(var(--mdui-color-primary));
}
.banner-title-text1 {
color: rgb(var(--mdui-color-surface-container-low));
}
.banner-title-text2 {
color: rgb(var(--mdui-color-surface-container-low));
}
.post-card-wide {
background-color: rgb(var(--mdui-color-surface-container-low));
}
.article-content {
color: rgb(var(--mdui-color-on-surface));
}
.archives-page {
max-width: 900px;
margin: 0 auto;
padding: 40px 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
.archives-header {
width: 100%;
text-align: center;
padding: 40px 32px;
border-radius: 24px;
background-color: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
}
.archives-year-card {
width: 100%;
border-radius: 20px;
overflow: hidden;
background-color: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface));
}
.archives-year-header {
padding: 24px 32px;
background: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
}
.archives-year-content {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 20px;
}
.archives-year-info {
display: flex;
flex-direction: column;
align-items: flex-start;
flex: 1;
}
.archives-post-list {
display: flex;
flex-direction: column;
padding: 0;
}
.archives-post-item {
display: flex;
align-items: center;
gap: 16px;
padding: 20px 32px;
text-decoration: none;
color: inherit;
transition: all 0.2s ease;
border-bottom: 1px solid rgb(var(--mdui-color-outline-variant));
}
.archives-post-item:last-child {
border-bottom: none;
}
.archives-post-item:hover {
background-color: rgb(var(--mdui-color-surface-container-highest));
transform: translateX(4px);
}
.archives-post-content {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
}
.archives-post-title {
font-weight: 500;
margin-bottom: 6px;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.archives-post-meta {
display: flex;
align-items: center;
gap: 12px;
font-size: 14px;
opacity: 0.7;
line-height: 1.4;
flex-wrap: wrap;
}
.archives-post-categories {
display: flex;
align-items: center;
gap: 8px;
}
.archives-post-category {
background: rgb(var(--mdui-color-surface-container-highest));
padding: 2px 8px;
border-radius: 6px;
font-size: 12px;
}
.archives-post-arrow {
flex-shrink: 0;
color: rgb(var(--mdui-color-outline));
transition: transform 0.2s ease;
}
.archives-post-item:hover .archives-post-arrow {
transform: translateX(4px);
}
.archives-empty-state {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin-top: 40px;
}
.archives-empty-card {
width: 100%;
max-width: 500px;
padding: 60px 40px;
text-align: center;
border-radius: 24px;
display: flex;
flex-direction: column;
align-items: center;
background-color: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface));
}
.archives-empty-icon {
font-size: 80px;
margin-bottom: 24px;
opacity: 0.6;
color: rgb(var(--mdui-color-primary-container));
}
.archives-empty-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
/* 平板设备 */
@media (max-width: 1024px) {
.archives-page {
padding: 32px 20px;
gap: 20px;
}
.archives-header {
padding: 32px 24px;
}
.archives-year-header {
padding: 20px 24px;
}
.archives-post-item {
padding: 18px 24px;
}
}
/* 手机设备 */
@media (max-width: 768px) {
.archives-page {
padding: 24px 16px;
gap: 16px;
}
.archives-header {
padding: 24px 20px;
}
.archives-year-header {
padding: 16px 20px;
}
.archives-year-content {
gap: 16px;
}
.archives-post-item {
padding: 16px 20px;
gap: 12px;
}
.archives-post-meta {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.archives-post-categories {
gap: 6px;
}
.archives-empty-card {
padding: 40px 24px;
}
.archives-empty-icon {
font-size: 60px;
margin-bottom: 20px;
}
}
/* 小屏手机 */
@media (max-width: 480px) {
.archives-page {
padding: 20px 12px;
gap: 12px;
}
.archives-header {
padding: 20px 16px;
}
.archives-year-header {
padding: 16px;
}
.archives-year-content {
flex-direction: column;
align-items: flex-start;
gap: 12px;
text-align: left;
}
.archives-year-info {
align-items: flex-start;
}
.archives-post-item {
padding: 14px 16px;
gap: 10px;
}
.archives-post-title {
font-size: 15px;
}
.archives-post-meta {
font-size: 13px;
}
}
</style>
<div class="archives-page">
<!-- 页面标题 -->
<mdui-card class="mdui-primary archives-header">
<mdui-typography variant="h3" style="margin-bottom: 12px; font-weight: 500;"><%= __('diary.title') %></mdui-typography>
<mdui-typography variant="h6" style="opacity: 0.8; font-weight: 400;">
<%= __('diary.total') %> <mdui-badge style="margin: 0 8px;"><%= site.posts.length %></mdui-badge> <%= __('diary.articles') %>
</mdui-typography>
</mdui-card>
<!-- 按年份分组 -->
<%
const postsByYear = {};
site.posts.sort('-date').each(function(post) {
const year = post.date.year();
if (!postsByYear[year]) {
postsByYear[year] = [];
}
postsByYear[year].push(post);
});
%>
<% Object.keys(postsByYear).sort().reverse().forEach(function(year) { %>
<!-- 年份卡片 -->
<mdui-card class="mdui-primary archives-year-card">
<!-- 年份标题 -->
<div class="archives-year-header">
<div class="archives-year-content">
<mdui-avatar variant="rounded" size="50px" class="archives-year-avatar">
<mdui-icon>calendar_today</mdui-icon>
</mdui-avatar>
<div class="archives-year-info">
<mdui-typography variant="h5" style="font-weight: 500;">
<%= year %> <%= __('diary.year') %>
</mdui-typography>
<mdui-typography variant="body" style="opacity: 0.8;">
<%= postsByYear[year].length %> <%= __('diary.articles') %>
</mdui-typography>
</div>
</div>
</div>
<!-- 文章列表 -->
<div class="archives-post-list">
<% postsByYear[year].forEach(function(post, index) { %>
<a href="<%= url_for(post.path) %>" class="archives-post-item">
<mdui-icon class="archives-post-icon">article</mdui-icon>
<div class="archives-post-content">
<div class="archives-post-title">
<%= post.title %>
</div>
<div class="archives-post-meta">
<span><%= post.date.format('MM-DD') %></span>
<% if (post.categories && post.categories.length) { %>
<div class="archives-post-categories">
<span>·</span>
<% post.categories.forEach(function(cat, catIndex) { %>
<span class="archives-post-category"><%= cat.name %></span>
<% }); %>
</div>
<% } %>
</div>
</div>
<mdui-icon class="archives-post-arrow">chevron_right</mdui-icon>
</a>
<% }); %>
</div>
</mdui-card>
<% }); %>
<!-- 空状态 -->
<% if (!site.posts || site.posts.length === 0) { %>
<div class="archives-empty-state">
<mdui-card class="mdui-primary archives-empty-card">
<mdui-icon class="archives-empty-icon">article</mdui-icon>
<div class="archives-empty-content">
<mdui-typography variant="h5" style="margin-bottom: 8px; font-weight: 500;">
<%= __('diary.no_articles') %>
</mdui-typography>
<mdui-typography variant="body" style="opacity: 0.7; line-height: 1.5;">
<%= __('diary.no_articles_desc') %>
</mdui-typography>
</div>
</mdui-card>
</div>
<% } %>
</div>

View File

@ -0,0 +1,8 @@
<%- include('widgets/sidebar') %>
<% if (theme.links.use == 0) { %>
<p><%= __('friend.no_config') %></p>
<% } else if (theme.links.use == 1) { %>
<%- include('widgets/index/friend_qexo.ejs') %>
<% } else if (theme.links.use == 2) { %>
<%- include('widgets/index/friend_links.ejs') %>
<% } %>

View File

@ -0,0 +1,30 @@
<div class="banner-wrapper">
<%- include('widgets/banner', {titleText1: theme.banner.title, titleText2: theme.banner.subtitle}) %>
</div>
<mdui-layout-main>
<div class="articles-container">
<%
const sortedPosts = Array.from(site.posts.data || site.posts).sort((a, b) => {
const dateA = a.date ? new Date(a.date).getTime() : new Date(a.updated).getTime();
const dateB = b.date ? new Date(b.date).getTime() : new Date(b.updated).getTime();
return dateB - dateA;
});
%>
<% if (sortedPosts && sortedPosts.length > 0) { %>
<% sortedPosts.forEach((post) => { %>
<%- include('widgets/index/post-card', {post: post}) %>
<% }); %>
<% } else { %>
<mdui-card class="no-articles" variant="filled">
<mdui-typography variant="h4" style="margin-bottom: 1rem; color: var(--mdui-color-on-surface);">
<%= __('index.no_articles') %>
</mdui-typography>
<mdui-typography variant="body1" style="color: var(--mdui-color-on-surface-variant);">
<%= __('index.no_articles_desc') %>
</mdui-typography>
</mdui-card>
<% } %>
</div>
</mdui-layout-main>

View File

@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="<%- config.language %>"">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://unpkg.com/mdui@2/mdui.css">
<script src="https://unpkg.com/mdui@2/mdui.global.js"></script>
<link rel="stylesheet" href="https://lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.css">
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<script src="//lib.baomitu.com/fancybox/3.5.7/jquery.fancybox.min.js"> </script>
<% if (theme.font.primary === 'lxwk') { %>
<link rel="stylesheet" href="/fonts/lxwk/lxwk.css">
<% } else if (theme.font.primary === 'dingtalk') { %>
<link rel="stylesheet" href="/fonts/dingtalk/DingTalk.css">
<% } else if (theme.font.primary === 'pingfang') { %>
<link rel="stylesheet" href="/fonts/pingfang/pingfang.css">
<% } %>
<%- css('css/style.css') %>
<%- css('css/color.css') %>
<%- css('css/widgets/page-transition.css') %>
<% if (theme.interceptor.enable) { %>
<%- include('widgets/links.ejs') %>
<% } %>
</head>
<body>
<%- include('widgets/appbar') %>
<%- include('widgets/sidebar') %>
<main class="main">
<div class="page-transition-container">
<%- body %>
</div>
</main>
<%- include('widgets/footer') %>
</body>
<script type="module" src="/js/main.js"></script>
<script src="/js/widgets/appbar.js"></script>
<script src="/js/widgets/sidebar.js"></script>
<script src="/js/page-transition.js"></script>
<script>
console.log(`
███╗ ███╗ █████╗ ████████╗███████╗██████╗ ██╗ █████╗ ██╗ ██╗███████╗
████╗ ████║██╔══██╗╚══██╔══╝██╔════╝██╔══██╗██║██╔══██╗██║ ██║██╔════╝
██╔████╔██║███████║ ██║ █████╗ ██████╔╝██║███████║██║ ██║███████╗
██║╚██╔╝██║██╔══██║ ██║ ██╔══╝ ██╔══██╗██║██╔══██║██║ ██║╚════██║
██║ ╚═╝ ██║██║ ██║ ██║ ███████╗██║ ██║██║██║ ██║███████╗██║███████║
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚══════╝
`);
</script>
<!-- 引入自定义 CSS -->
<% if (theme.custom && theme.custom.css) { %>
<% theme.custom.css.forEach(function(cssFile) { %>
<link rel="stylesheet" href="<%- url_for(cssFile) %>">
<% }) %>
<% } %>
<!-- 自定义头部代码 -->
<% if (theme.custom && theme.custom.head) { %>
<%- theme.custom.head %>
<% } %>
<!-- 引入自定义 JS -->
<% if (theme.custom && theme.custom.js) { %>
<% theme.custom.js.forEach(function(jsFile) { %>
<script src="<%- url_for(jsFile) %>"></script>
<% }) %>
<% } %>
<!-- 自定义底部代码 -->
<% if (theme.custom && theme.custom.footer) { %>
<%- theme.custom.footer %>
<% } %>
<!-- 纪念日黑白效果 -->
<% if (theme.memorial_days && theme.memorial_days.enable) { %>
<script>
(function() {
// 获取今天的日期(月-日格式)
const today = new Date();
const month = today.getMonth() + 1; // 月份从0开始需要+1
const day = today.getDate();
const todayStr = month + '.' + day;
// 获取配置的纪念日列表
const memorialDays = JSON.parse('<%- JSON.stringify(theme.memorial_days.dates || []) %>');
// 检查今天是否为纪念日
if (memorialDays.includes(todayStr)) {
// 应用黑白效果
const style = document.createElement('style');
style.innerHTML = `
html {
filter: grayscale(100%);
-webkit-filter: grayscale(100%);
-moz-filter: grayscale(100%);
-ms-filter: grayscale(100%);
-o-filter: grayscale(100%);
}
`;
document.head.appendChild(style);
}
})();
</script>
<% } %>
</html>

View File

@ -0,0 +1,671 @@
<%- js('js/fancybox.js') %>
<style>
.post-container {
max-width: 900px;
margin: 0 auto;
padding: 40px 20px;
display: flex;
flex-direction: column;
gap: 24px;
}
.post-card-wide {
border-radius: 20px;
background-color: rgb(var(--mdui-color-surface-container-low));
box-shadow: none;
}
.post-card-comment {
border-radius: 20px;
background-color: rgb(var(--mdui-color-surface-container-low));
box-shadow: none;
padding: 2.5rem;
}
.post-card-nav {
border-radius: 20px;
background-color: rgb(var(--mdui-color-surface-container-low));
box-shadow: none;
padding: 2.5rem;
}
.markdown-section img,
.article-content img {
max-width: 100%;
height: auto;
display: block;
margin: 1.2em auto;
object-fit: contain;
box-sizing: border-box;
border-radius: 12px;
}
.caption {
color: #444;
display: block;
font-size: 0.9em;
margin-top: 0.1em;
position: relative;
text-align: center;
}
.article-content {
color: rgb(var(--mdui-color-on-surface));
line-height: 1.8;
font-family: var(--mdui-font-family);
width: 100%;
overflow-wrap: break-word;
}
.article-content h1,
.article-content h2,
.article-content h3,
.article-content h4,
.article-content h5,
.article-content h6 {
color: rgb(var(--mdui-color-primary));
margin-top: 1.5em;
margin-bottom: 1em;
}
.article-content p {
margin-bottom: 1.2em;
}
.article-content a {
color: rgb(var(--mdui-color-primary));
text-decoration: none;
border-bottom: 1px dashed rgb(var(--mdui-color-primary));
}
.article-content a:hover {
opacity: 0.8;
}
.article-content code {
background-color: rgb(var(--mdui-color-surface-container-high));
padding: 2px 6px;
border-radius: 4px;
font-family: var(--mdui-font-family-mono);
word-break: break-word;
}
/* 修复代码块样式 - 关键修复 */
.mdui-code-block {
background: rgb(var(--mdui-color-surface-container-high));
border-radius: 12px;
margin: 1.5em 0;
overflow: hidden;
font-family: var(--mdui-font-family-mono);
width: 100%;
box-sizing: border-box;
/* 确保代码块不会超出容器 */
max-width: 100%;
}
.mdui-code-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: rgb(var(--mdui-color-surface-container-highest));
border-bottom: 1px solid rgb(var(--mdui-color-outline-variant));
position: relative;
width: 100%;
box-sizing: border-box;
flex-wrap: wrap;
gap: 8px;
}
.mdui-code-language {
color: rgb(var(--mdui-color-on-surface-variant));
font-size: 0.875rem;
font-weight: 500;
font-family: var(--mdui-font-family);
flex-shrink: 0;
}
.mdui-code-actions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;
}
.mdui-code-toggle {
color: rgb(var(--mdui-color-on-surface-variant));
cursor: pointer;
transition: color 0.2s ease;
flex-shrink: 0;
}
.mdui-code-toggle:hover {
color: rgb(var(--mdui-color-primary));
}
.mdui-code-copy {
color: rgb(var(--mdui-color-on-surface-variant));
cursor: pointer;
transition: color 0.2s ease;
flex-shrink: 0;
}
.mdui-code-copy:hover {
color: rgb(var(--mdui-color-primary));
}
.mdui-code-copy.copied {
color: rgb(var(--mdui-color-success));
}
/* 关键修复:代码内容区域 */
.mdui-code-content {
position: relative;
background: rgb(var(--mdui-color-surface-container-high));
width: 100%;
box-sizing: border-box;
/* 修复滚动问题 */
overflow: hidden;
-webkit-overflow-scrolling: touch;
/* 使用 flex 布局:左侧行号列 + 右侧代码内容 */
display: flex;
align-items: stretch;
}
/* 关键修复:代码内容本身 */
.mdui-code-pre {
margin: 0;
padding: 16px;
font-family: var(--mdui-font-family-mono);
font-size: 0.875rem;
line-height: 1.5;
color: rgb(var(--mdui-color-on-surface));
/* 关键:保持代码不换行 */
white-space: pre;
/* 关键:让内容自然扩展 */
display: block;
box-sizing: border-box;
/* 允许横向滚动,但代码区域使用 flex:1 */
flex: 1 1 auto;
min-width: 0; /* allow flex to shrink */
}
/* 行号 gutter */
.mdui-code-gutter {
padding: 12px 8px;
background: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface-variant));
text-align: right;
user-select: none;
display: flex;
flex-direction: column;
gap: 0;
flex: 0 0 auto;
}
.mdui-code-gutter .mdui-code-line-number {
display: block;
font-size: 0.8125rem;
line-height: 1.5;
min-height: 1.5em;
padding: 0 4px;
}
/* 自定义滚动条样式 */
.mdui-code-content::-webkit-scrollbar {
height: 8px;
}
.mdui-code-content::-webkit-scrollbar-track {
background: rgb(var(--mdui-color-surface-container));
border-radius: 0 0 4px 4px;
}
.mdui-code-content::-webkit-scrollbar-thumb {
background: rgb(var(--mdui-color-outline-variant));
border-radius: 4px;
}
.mdui-code-content::-webkit-scrollbar-thumb:hover {
background: rgb(var(--mdui-color-outline));
}
/* Firefox 滚动条样式 */
.mdui-code-content {
scrollbar-width: thin;
scrollbar-color: rgb(var(--mdui-color-outline-variant)) rgb(var(--mdui-color-surface-container));
}
.mdui-code-block.collapsed .mdui-code-content {
display: none;
}
.mdui-code-block.collapsed .mdui-code-toggle .expand-icon {
display: inline;
}
.mdui-code-block.collapsed .mdui-code-toggle .collapse-icon {
display: none;
}
.mdui-code-block:not(.collapsed) .mdui-code-toggle .expand-icon {
display: none;
}
.mdui-code-block:not(.collapsed) .mdui-code-toggle .collapse-icon {
display: inline;
}
.article-content pre {
background-color: rgb(var(--mdui-color-surface-container-high));
padding: 16px;
border-radius: 8px;
overflow-x: auto;
width: 100%;
box-sizing: border-box;
}
.article-content blockquote {
border-left: 4px solid rgb(var(--mdui-color-primary));
padding: 8px 16px;
margin: 16px 0;
background-color: rgb(var(--mdui-color-surface-container));
width: 100%;
box-sizing: border-box;
}
.article-content ul,
.article-content ol {
padding-left: 32px;
margin-bottom: 16px;
width: 100%;
}
.article-content li {
margin-bottom: 8px;
word-break: break-word;
}
.post-navigation {
display: flex;
justify-content: space-between;
gap: 16px;
padding: 1rem 0;
}
.nav-button {
flex: 1;
text-align: center;
padding: 16px;
border-radius: 12px;
background-color: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface));
text-decoration: none;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
gap: 8px;
box-sizing: border-box;
}
.nav-button:hover {
background-color: rgb(var(--mdui-color-surface-container-high));
transform: translateY(-2px);
}
.nav-button.disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
.nav-button.disabled:hover {
background-color: rgb(var(--mdui-color-surface-container));
transform: none;
}
.nav-label {
font-size: 0.875rem;
color: rgb(var(--mdui-color-on-surface-variant));
}
.nav-title {
font-weight: 500;
}
/* 平板设备 */
@media (max-width: 1024px) {
.post-container {
padding: 2rem 24px;
gap: 20px;
}
.post-card-wide,
.post-card-comment,
.post-card-nav {
padding: 2rem;
}
}
/* 手机设备 */
@media (max-width: 768px) {
.post-container {
padding: 24px 16px;
gap: 16px;
}
.post-card-wide {
padding: 1.5rem;
}
.post-card-comment,
.post-card-nav {
padding: 1.5rem;
}
.post-navigation {
flex-direction: column;
gap: 12px;
}
.mdui-code-header {
padding: 10px 12px;
}
.mdui-code-pre {
padding: 12px;
font-size: 0.8125rem;
}
.mdui-code-content::-webkit-scrollbar {
height: 6px;
}
/* 移动端代码块特殊处理 */
.mdui-code-block {
margin: 1em 0;
border-radius: 8px;
}
.article-content {
font-size: 0.9375rem;
}
}
/* 小屏手机 */
@media (max-width: 480px) {
.post-container {
padding: 20px 12px;
gap: 12px;
}
.post-card-wide,
.post-card-comment,
.post-card-nav {
padding: 1rem;
border-radius: 16px;
}
.mdui-code-pre {
padding: 10px;
font-size: 0.75rem;
}
.article-content ul,
.article-content ol {
padding-left: 24px;
}
.mdui-code-header {
padding: 8px 12px;
}
.mdui-code-language {
font-size: 0.75rem;
}
}
</style>
<mdui-layout-main>
<%- include('widgets/banner', {titleText1: page.title, titleText2: ''}) %>
<div class="post-container">
<!-- 内容卡片 -->
<mdui-card variant="filled" class="post-card-wide">
<mdui-card-content>
<div class="article-content">
<%- page.content %>
</div>
</mdui-card-content>
</mdui-card>
<!-- 上一篇/下一篇导航 -->
<mdui-card variant="filled" class="post-card-nav">
<mdui-card-content>
<div class="post-navigation">
<% if (page.prev) { %>
<a href="<%= url_for(page.prev.path) %>" class="nav-button">
<span class="nav-label"><%= __('post.prev') %></span>
<span class="nav-title"><%= page.prev.title %></span>
</a>
<% } else { %>
<a class="nav-button disabled">
<span class="nav-label"><%= __('post.prev') %></span>
<span class="nav-title"><%= __('post.none') %></span>
</a>
<% } %>
<% if (page.next) { %>
<a href="<%= url_for(page.next.path) %>" class="nav-button">
<span class="nav-label"><%= __('post.next') %></span>
<span class="nav-title"><%= page.next.title %></span>
</a>
<% } else { %>
<a class="nav-button disabled">
<span class="nav-label"><%= __('post.next') %></span>
<span class="nav-title"><%= __('post.none') %></span>
</a>
<% } %>
</div>
</mdui-card-content>
</mdui-card>
<!-- 评论卡片 -->
<mdui-card variant="filled" class="post-card-comment">
<mdui-card-content>
<mdui-typography variant="h5" style="margin-bottom: 1.5rem; text-align: center; color: rgb(var(--mdui-color-primary));">
<%= __('post.comment') %>
</mdui-typography>
<% if (theme.comment && theme.comment.service) { %>
<% if (theme.comment.service === 'twikoo') { %>
<%- include('widgets/comments/twikoo.ejs') %>
<% } else if (theme.comment.service === 'waline') { %>
<%- include('widgets/comments/waline.ejs') %>
<% } else if (theme.comment.service === 'giscus') { %>
<%- include('widgets/comments/giscus.ejs') %>
<% } else { %>
<div style="text-align: center; padding: 2rem;">
<mdui-typography variant="body2" style="color: var(--mdui-color-on-surface-variant);">
<%= __('post.comment_disabled') %>
</mdui-typography>
</div>
<% } %>
<% } else { %>
<div style="text-align: center; padding: 2rem;">
<mdui-typography variant="body2" style="color: var(--mdui-color-on-surface-variant);">
<%= __('post.comment_disabled') %>
</mdui-typography>
</div>
<% } %>
</mdui-card-content>
</mdui-card>
</div>
</mdui-layout-main>
<script>
// 代码块功能增强(重构版)
(function(){
function detectLanguage(pre){
// 优先考虑 data-language 属性
if (pre.dataset && pre.dataset.language) return pre.dataset.language;
const code = pre.querySelector('code');
const cls = code && code.className ? code.className : '';
const m = cls.match(/language-([A-Za-z0-9_\-]+)/) || cls.match(/lang-([A-Za-z0-9_\-]+)/);
if (m) return m[1];
return 'Code';
}
function makeHeader(language){
const header = document.createElement('div');
header.className = 'mdui-code-header';
const langSpan = document.createElement('span');
langSpan.className = 'mdui-code-language';
langSpan.textContent = language;
const actions = document.createElement('div');
actions.className = 'mdui-code-actions';
const toggle = document.createElement('button');
toggle.type = 'button';
toggle.className = 'mdui-code-toggle';
toggle.setAttribute('aria-label', 'Toggle code');
toggle.innerHTML = '<mdui-icon class="collapse-icon" size="small">unfold_less</mdui-icon><mdui-icon class="expand-icon" size="small">unfold_more</mdui-icon>';
const copy = document.createElement('button');
copy.type = 'button';
copy.className = 'mdui-code-copy';
copy.setAttribute('aria-label', 'Copy code');
copy.innerHTML = '<mdui-icon size="small">content_copy</mdui-icon>';
actions.appendChild(toggle);
actions.appendChild(copy);
header.appendChild(langSpan);
header.appendChild(actions);
return header;
}
function processPre(pre){
if (pre.closest('.mdui-code-block')) return;
const language = detectLanguage(pre);
const codeBlock = document.createElement('div');
codeBlock.className = 'mdui-code-block';
const header = makeHeader(language);
const content = document.createElement('div');
content.className = 'mdui-code-content';
// 移动原始 pre 元素到新的容器,保留任何高亮 markup
pre.classList.add('mdui-code-pre');
// 生成行号 gutter不包含在复制内容中
try {
const codeText = pre.textContent || pre.innerText || '';
const lines = codeText.split(/\r\n|\r|\n/);
const gutter = document.createElement('div');
gutter.className = 'mdui-code-gutter';
for (let i = 0; i < lines.length; i++) {
const ln = document.createElement('span');
ln.className = 'mdui-code-line-number';
ln.textContent = (i + 1);
gutter.appendChild(ln);
}
content.appendChild(gutter);
} catch (e) {
// ignore gutter errors
}
content.appendChild(pre);
codeBlock.appendChild(header);
codeBlock.appendChild(content);
// 替换原位置pre 将被移动到新的 content 中)
if (pre.parentNode) {
pre.parentNode.replaceChild(codeBlock, pre);
} else {
// 保险回退:尝试查找原来应该替换的位置
const sibling = document.querySelector('body');
// 最后手段:附加到 body极少出现
sibling && sibling.appendChild(codeBlock);
}
}
function init(){
document.querySelectorAll('pre').forEach(processPre);
// 事件委托:折叠/展开 & 复制
document.addEventListener('click', function(e){
const toggle = e.target.closest('.mdui-code-toggle');
if (toggle) {
const block = toggle.closest('.mdui-code-block');
block && block.classList.toggle('collapsed');
return;
}
const copyBtn = e.target.closest('.mdui-code-copy');
if (copyBtn) {
const block = copyBtn.closest('.mdui-code-block');
if (!block) return;
const pre = block.querySelector('.mdui-code-pre');
const codeText = pre ? pre.textContent : '';
// 复制并给出视觉反馈
navigator.clipboard && navigator.clipboard.writeText
? navigator.clipboard.writeText(codeText).then(() => flashCopy(copyBtn))
: fallbackCopy(codeText, copyBtn);
}
});
// 初始移动端修复
function fixCodeBlocksOnMobile(){
document.querySelectorAll('.mdui-code-block').forEach(block => {
const content = block.querySelector('.mdui-code-content');
const pre = block.querySelector('.mdui-code-pre');
if (content && pre) {
content.style.overflowX = 'auto';
pre.style.whiteSpace = 'pre';
pre.style.display = 'inline-block';
pre.style.minWidth = 'min-content';
}
});
}
function flashCopy(button){
const original = button.innerHTML;
button.innerHTML = '<mdui-icon size="small" style="color: var(--mdui-color-success)">check</mdui-icon>';
button.classList.add('copied');
setTimeout(()=>{ button.innerHTML = original; button.classList.remove('copied'); }, 2000);
}
function fallbackCopy(text, button){
try {
const ta = document.createElement('textarea');
ta.value = text;
ta.style.position = 'fixed'; ta.style.left = '-9999px';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
flashCopy(button);
} catch (err) {
console.error('Fallback copy failed', err);
}
}
fixCodeBlocksOnMobile();
window.addEventListener('resize', fixCodeBlocksOnMobile);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>

View File

@ -0,0 +1,8 @@
<%- include('widgets/sidebar') %>
<% if (theme.shuoshuo.use == 0) { %>
<p><%= __('shuoshuo.no_config') %></p>
<% } else if (theme.shuoshuo.use == 1) { %>
<%- include('widgets/index/shuoshuo_qexo.ejs') %>
<% } else if (theme.shuoshuo.use == 2) { %>
<%- include('widgets/index/shuoshuo_theme.ejs') %>
<% } %>

View File

@ -0,0 +1,204 @@
<style>
.tags-page {
max-width: 900px;
margin: 0 auto;
padding: 40px 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
.tags-header {
width: 100%;
text-align: center;
padding: 40px 32px;
border-radius: 24px;
background-color: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
}
.tags-cloud {
width: 100%;
background-color: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface));
border-radius: 20px;
padding: 32px;
}
.tags-container {
display: flex;
flex-wrap: wrap;
gap: 12px;
justify-content: center;
}
.tag-chip {
--mdui-chip-height: 36px;
--mdui-chip-container-color: var(--mdui-color-secondary-container);
--mdui-chip-label-text-color: var(--mdui-color-on-secondary-container);
border-radius: 18px;
transition: all 0.2s ease;
}
.tag-chip:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
--mdui-chip-container-color: var(--mdui-color-primary-container);
--mdui-chip-label-text-color: var(--mdui-color-on-primary-container);
}
.tags-empty-state {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin-top: 40px;
}
.tags-empty-card {
width: 100%;
max-width: 500px;
padding: 60px 40px;
text-align: center;
border-radius: 24px;
display: flex;
flex-direction: column;
align-items: center;
background-color: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface));
}
.tags-empty-icon {
font-size: 80px;
margin-bottom: 24px;
opacity: 0.6;
color: rgb(var(--mdui-color-primary-container));
}
.tags-empty-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
/* 平板设备 */
@media (max-width: 1024px) {
.tags-page {
padding: 32px 20px;
gap: 20px;
}
.tags-header {
padding: 32px 24px;
}
.tags-cloud {
padding: 24px;
}
}
/* 手机设备 */
@media (max-width: 768px) {
.tags-page {
padding: 24px 16px;
gap: 16px;
}
.tags-header {
padding: 24px 20px;
}
.tags-cloud {
padding: 20px;
}
.tags-container {
gap: 10px;
}
.tags-empty-card {
padding: 40px 24px;
}
.tags-empty-icon {
font-size: 60px;
margin-bottom: 20px;
}
}
/* 小屏手机 */
@media (max-width: 480px) {
.tags-page {
padding: 20px 12px;
gap: 12px;
}
.tags-header {
padding: 20px 16px;
}
.tags-cloud {
padding: 16px;
}
.tags-container {
gap: 8px;
}
.tag-chip {
--mdui-chip-height: 32px;
font-size: 14px;
}
}
</style>
<div class="tags-page">
<!-- 页面标题 -->
<mdui-card class="tags-header">
<mdui-typography variant="h3" style="margin-bottom: 12px; font-weight: 500;">
<%= __('tags.title') %>
</mdui-typography>
<mdui-typography variant="h6" style="opacity: 0.8; font-weight: 400;">
<% if (site.tags.length > 0) { %>
<%= __('tags.count_prefix') %> <mdui-badge style="margin: 0 8px;"><%= site.tags.length %></mdui-badge> <%= __('tags.count_suffix') %>
<% } else { %>
<%= __('tags.empty') %>
<% } %>
</mdui-typography>
</mdui-card>
<!-- 标签云 -->
<% if (site.tags.length > 0) { %>
<mdui-card class="tags-cloud">
<div class="tags-container">
<% site.tags.sort('name').each(function(tag) { %>
<mdui-chip
class="tag-chip"
href="<%- url_for(tag.path) %>"
variant="filled"
>
<%= tag.name %>
<mdui-badge slot="end"><%= tag.length %></mdui-badge>
</mdui-chip>
<% }); %>
</div>
</mdui-card>
<% } else { %>
<!-- 空状态 -->
<div class="tags-empty-state">
<mdui-card class="tags-empty-card">
<mdui-icon class="tags-empty-icon">local_offer</mdui-icon>
<div class="tags-empty-content">
<mdui-typography variant="h5" style="margin-bottom: 8px; font-weight: 500;">
<%= __('tags.empty') %>
</mdui-typography>
<mdui-typography variant="body" style="opacity: 0.7; line-height: 1.5;">
<%= __('tags.empty_desc') %>
</mdui-typography>
</div>
</mdui-card>
</div>
<% } %>
</div>

View File

@ -0,0 +1,31 @@
<mdui-top-app-bar variant="center-aligned" scroll-behavior="elevate" class="top-app-bar">
<mdui-button-icon class="appbar-menu-button">
<mdui-icon name="menu"></mdui-icon>
</mdui-button-icon>
<mdui-top-app-bar-title onclick="window.location.href='<%= url_for('/') %>'" style="cursor: pointer;">
<%= config.title %>
</mdui-top-app-bar-title>
<mdui-dropdown>
<div slot="trigger">
<mdui-tooltip>
<mdui-button-icon>
<mdui-icon name="auto_mode" class="appbar-display-mode-icon"></mdui-icon>
</mdui-button-icon>
</mdui-tooltip>
</div>
<mdui-menu selects="single" value="auto" class="appbar-display-mode">
<mdui-menu-item value="light"><%= __('appbar.light_mode') %></mdui-menu-item>
<mdui-menu-item value="dark"><%= __('appbar.dark_mode') %></mdui-menu-item>
<mdui-divider></mdui-divider>
<mdui-menu-item value="auto"><%= __('appbar.auto_mode') %></mdui-menu-item>
</mdui-menu>
</mdui-dropdown>
<%- partial('widgets/search.ejs') %>
<%- partial('widgets/color.ejs') %>
<% if (page && page.layout === 'post') { %>
<%- partial('widgets/nav.ejs') %>
<%- include('share.ejs') %>
<% } %>
</mdui-top-app-bar>

View File

@ -0,0 +1,89 @@
<link rel="stylesheet" href="/fonts/banner/banner.css">
<div class="banner-container">
<mdui-card class="banner-title">
<div class="banner-title-text1"><%- titleText1 %></div>
<div class="banner-title-text2"><%- titleText2 %></div>
<% if (get_path() === '/post/') { %>
<mdui-chip variant="filled">
<mdui-icon name="calendar_today" slot="icon"></mdui-icon>
<%= page.date.format('YYYY-MM-DD') %>
</mdui-chip>
<% if (page.categories && page.categories.length > 0) { %>
<mdui-chip variant="filled">
<mdui-icon name="folder" slot="icon"></mdui-icon>
<%= page.categories.data[0].name %>
</mdui-chip>
<% } %>
<% if (page.tags && page.tags.length > 0) { %>
<mdui-chip variant="filled">
<mdui-icon name="tag" slot="icon"></mdui-icon>
<%= page.tags.data[0].name %>
</mdui-chip>
<% } %>
<% } %>
</mdui-card>
<mdui-card class="banner-image">
<div style="margin: 16px; text-align: center;">
<p class="banner-hitokoto" variant="filled">
<mdui-icon slot="icon">format_quote</mdui-icon>
<a href="#" id="hitokoto_text" style="color: var(--mdui-color-on-surface); text-decoration: none; font-style: italic;">
<span id="hitokoto_inner">获取中...</span><span id="hitokoto_cursor">|</span>
</a>
</p>
</div>
<script>
(function(){
function typeText(el, text, speed, cursorEl) {
if (!el) return;
el.textContent = '';
let i = 0;
if (cursorEl) cursorEl.style.opacity = '1';
function step(){
if (i < text.length) {
el.textContent += text.charAt(i);
i++;
setTimeout(step, speed);
} else {
if (cursorEl) cursorEl.style.opacity = '0';
}
}
step();
}
function fetchHitokoto(){
fetch('https://v1.hitokoto.cn')
.then(function(r){ return r.json(); })
.then(function(data){
var inner = document.getElementById('hitokoto_inner');
var cursor = document.getElementById('hitokoto_cursor');
var link = document.getElementById('hitokoto_text');
if (!inner || !link) return;
if (data && data.hitokoto) {
var text = data.hitokoto + (data.from ? ' — ' + data.from : '');
if (data.uuid) link.href = 'https://hitokoto.cn/?uuid=' + data.uuid;
typeText(inner, text, 40, cursor);
}
})
.catch(function(){ /* ignore errors */ });
}
// 添加简单的光标闪烁样式
var _s = document.createElement('style');
_s.textContent = '#hitokoto_cursor{display:inline-block;margin-left:6px;opacity:1;transition:opacity .2s}@keyframes _hk{0%{opacity:1}50%{opacity:0}100%{opacity:1}} #hitokoto_cursor{animation:_hk 1s steps(1) infinite}';
document.head.appendChild(_s);
if (document.readyState === 'complete' || document.readyState === 'interactive') {
fetchHitokoto();
} else {
document.addEventListener('DOMContentLoaded', fetchHitokoto);
}
})();
</script>
</mdui-card>
</div>

View File

@ -0,0 +1,323 @@
<!-- 动态配色选择器组件 -->
<mdui-button-icon id="color-picker-trigger" class="color-picker-icon">
<mdui-icon name="palette"></mdui-icon>
<mdui-tooltip content="动态配色"></mdui-tooltip>
</mdui-button-icon>
<mdui-dialog id="color-picker-dialog" class="dynamic-color-dialog" close-on-overlay-click>
<div class="mdui-dialog-title">动态配色设置</div>
<div class="mdui-dialog-content">
<!-- 颜色选择 -->
<div class="color-section">
<h4>选择颜色</h4>
<div class="color-picker-container">
<input type="color" id="native-color-picker" class="native-color-picker" value="#6750a4">
<mdui-button id="random-color-btn" variant="tonal">随机颜色</mdui-button>
</div>
<!-- 颜色预览 -->
<div class="color-preview">
<div class="preview-label">预览:</div>
<div id="color-preview" class="preview-sample"></div>
</div>
</div>
<mdui-divider></mdui-divider>
<!-- 操作按钮 -->
<div class="color-section">
<div class="action-buttons">
<mdui-button id="reset-color-btn" variant="tonal">重置</mdui-button>
</div>
</div>
</div>
<div class="mdui-dialog-actions">
<mdui-button id="cancel-color-btn">取消</mdui-button>
<mdui-button variant="filled" id="apply-color-btn">应用</mdui-button>
</div>
</mdui-dialog>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取DOM元素
const colorPickerTrigger = document.getElementById('color-picker-trigger');
const colorDialog = document.getElementById('color-picker-dialog');
const cancelBtn = document.getElementById('cancel-color-btn');
const applyBtn = document.getElementById('apply-color-btn');
const nativeColorPicker = document.getElementById('native-color-picker');
const resetBtn = document.getElementById('reset-color-btn');
const randomBtn = document.getElementById('random-color-btn');
const colorPreview = document.getElementById('color-preview');
let selectedColor = nativeColorPicker.value;
// 本地存储键名
const COLOR_SCHEME_KEY = 'materialis-color-scheme';
// 页面加载时恢复保存的颜色主题
function loadSavedColorScheme() {
try {
const savedColor = localStorage.getItem(COLOR_SCHEME_KEY);
if (savedColor && isValidColor(savedColor)) {
applyColorScheme(savedColor);
nativeColorPicker.value = savedColor;
updateColorPreview(savedColor);
selectedColor = savedColor;
}
} catch (e) {
console.warn('无法从本地存储加载颜色主题:', e);
}
}
// 保存颜色主题到本地存储
function saveColorScheme(color) {
try {
if (color && isValidColor(color)) {
localStorage.setItem(COLOR_SCHEME_KEY, color);
} else {
localStorage.removeItem(COLOR_SCHEME_KEY);
}
} catch (e) {
console.warn('无法保存颜色主题到本地存储:', e);
}
}
// 应用颜色主题
function applyColorScheme(color) {
if (typeof mdui !== 'undefined' && mdui.setColorScheme) {
mdui.setColorScheme(color);
} else {
console.error('MDUI setColorScheme 函数未找到');
}
}
// 移除颜色主题
function removeColorScheme() {
if (typeof mdui !== 'undefined' && mdui.removeColorScheme) {
mdui.removeColorScheme();
}
}
// 打开对话框
colorPickerTrigger.addEventListener('click', function() {
colorDialog.open = true;
});
// 关闭对话框
cancelBtn.addEventListener('click', function() {
colorDialog.open = false;
resetSelection();
});
// 原生颜色选择器事件
nativeColorPicker.addEventListener('input', function() {
selectedColor = this.value;
updateColorPreview(selectedColor);
});
// 应用配色
applyBtn.addEventListener('click', function() {
if (selectedColor && isValidColor(selectedColor)) {
// 应用并保存配色方案
applyColorScheme(selectedColor);
saveColorScheme(selectedColor);
// 显示成功提示
mdui.snackbar({
message: '配色已应用',
placement: 'bottom',
timeout: 2000
});
colorDialog.open = false;
resetSelection();
} else {
mdui.snackbar({
message: '请输入有效的颜色值',
placement: 'bottom',
timeout: 2000
});
}
});
// 重置配色
resetBtn.addEventListener('click', function() {
removeColorScheme();
saveColorScheme(null);
mdui.snackbar({
message: '配色已重置',
placement: 'bottom',
timeout: 2000
});
resetSelection();
});
// 随机颜色
randomBtn.addEventListener('click', function() {
const randomColor = '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0');
selectedColor = randomColor;
nativeColorPicker.value = randomColor;
updateColorPreview(randomColor);
});
// 辅助函数:验证颜色值
function isValidColor(color) {
return /^#([A-Fa-f0-9]{6})$/.test(color);
}
// 更新颜色预览
function updateColorPreview(color) {
if (colorPreview && isValidColor(color)) {
colorPreview.style.backgroundColor = color;
}
}
// 重置选择状态
function resetSelection() {
selectedColor = nativeColorPicker.value;
updateColorPreview(selectedColor);
}
// 页面加载时恢复保存的主题
loadSavedColorScheme();
});
</script>
<style>
.dynamic-color-dialog::part(overlay) {
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
background: rgba(0, 0, 0, 0.4);
}
.dynamic-color-dialog {
--mdui-dialog-width: 400px;
--mdui-dialog-max-height: 90vh;
}
.mdui-dialog-title {
text-align: center;
padding: 20px 20px 0;
font-weight: 500;
color: var(--mdui-color-on-surface);
font-size: 1.1rem;
}
.mdui-dialog-content {
padding: 16px 20px;
}
.mdui-dialog-actions {
padding: 12px 20px 20px;
justify-content: flex-end;
gap: 12px;
}
.color-section {
margin-bottom: 16px;
}
.color-section:last-child {
margin-bottom: 0;
}
.color-section h4 {
margin: 0 0 12px 0;
color: var(--mdui-color-on-surface);
font-size: 0.95rem;
font-weight: 500;
}
.color-picker-container {
display: flex;
gap: 12px;
align-items: center;
margin-bottom: 16px;
}
.native-color-picker {
width: 60px;
height: 40px;
border: none;
border-radius: 6px;
cursor: pointer;
background-color: var(--mdui-color-surface-container);
}
.native-color-picker::-webkit-color-swatch {
border: 1px solid var(--mdui-color-outline-variant);
border-radius: 6px;
}
.native-color-picker::-moz-color-swatch {
border: 1px solid var(--mdui-color-outline-variant);
border-radius: 6px;
}
.color-preview {
display: flex;
align-items: center;
gap: 12px;
margin-top: 8px;
}
.preview-label {
color: var(--mdui-color-on-surface-variant);
font-size: 0.85rem;
}
.preview-sample {
width: 60px;
height: 30px;
border-radius: 4px;
border: 1px solid var(--mdui-color-outline-variant);
background-color: transparent;
}
.action-buttons {
display: flex;
gap: 12px;
}
.color-picker-icon {
margin: 0 4px;
color: var(--mdui-color-on-surface-variant);
transition: all 0.2s ease;
}
.color-picker-icon:hover {
color: var(--mdui-color-primary);
}
/* 响应式设计 */
@media (max-width: 600px) {
.dynamic-color-dialog {
--mdui-dialog-width: 90vw;
}
.mdui-dialog-content {
padding: 12px 16px;
}
.mdui-dialog-actions {
padding: 12px 16px 16px;
}
}
.mdui-dialog-content::-webkit-scrollbar {
width: 6px;
}
.mdui-dialog-content::-webkit-scrollbar-track {
background: var(--mdui-color-surface-container);
}
.mdui-dialog-content::-webkit-scrollbar-thumb {
background: var(--mdui-color-outline-variant);
border-radius: 3px;
}
</style>

View File

@ -0,0 +1,15 @@
<script src="https://giscus.app/client.js"
data-repo="<%= config.giscus.repo %>"
data-repo-id="<%= config.giscus.repoId %>"
data-category="<%= config.giscus.category %>"
data-category-id="<%= config.giscus.categoryId %>"
data-mapping="<%= config.giscus.mapping %>"
data-strict="0"
data-reactions-enabled="<%= config.giscus.reactionsEnabled ? 1 : 0 %>"
data-emit-metadata="<%= config.giscus.emitMetadata ? 1 : 0 %>"
data-input-position="<%= config.giscus.inputPosition %>"
data-theme="preferred_color_scheme"
data-lang="<%= config.giscus.lang %>"
crossorigin="anonymous"
async>
</script>

View File

@ -0,0 +1,23 @@
<% if (theme.twikoo && theme.twikoo.envId) { %>
<div id="tcomment"></div>
<script src="https://cdn.jsdelivr.net/npm/twikoo@1.6.44/dist/twikoo.min.js"></script>
<script>
twikoo.init({
envId: '<%= theme.twikoo.envId %>',
el: '#tcomment',
// region: 'ap-guangzhou', // 腾讯云环境时需要Vercel 不需要
path: location.pathname,
lang: '<%= theme.twikoo.lang || "zh-CN" %>'
}).then(function () {
console.log('Twikoo 评论系统加载成功');
}).catch(function (err) {
console.error('Twikoo 评论系统加载失败:', err);
});
</script>
<% } else { %>
<div class="mdui-container" style="text-align: center; padding: 2rem;">
<mdui-typography variant="body2" style="color: var(--mdui-color-on-surface-variant);">
Twikoo 评论系统未正确配置,请检查您的设置。
</mdui-typography>
</div>
<% } %>

View File

@ -0,0 +1,17 @@
<!-- 样式文件 -->
<link
rel="stylesheet"
href="https://unpkg.com/@waline/client@v3/dist/waline.css"
/>
<!-- 评论容器 -->
<div id="waline"></div>
<!-- 脚本文件 -->
<script type="module">
import { init } from 'https://unpkg.com/@waline/client@v3/dist/waline.js';
init({
el: '#waline',
serverURL: '<%= config.waline.serverURL %>',
lang: '<%= config.waline.lang %>'
});
</script>

View File

@ -0,0 +1,240 @@
<br>
<mdui-divider></mdui-divider>
<mdui-footer class="site-footer">
<div class="footer-content">
<!-- 主要信息区域 -->
<div class="footer-main">
<!-- 版权信息 -->
<div class="copyright">
<mdui-chip variant="filled" class="footer-chip">
<mdui-icon name="copyright" slot="icon"></mdui-icon>
&copy; <%= new Date().getFullYear() %>
<a href="<%= url_for() %>" class="site-link"><%= config.title %></a>
<% if (theme.footer && theme.footer.copyright) { %>
· <%= theme.footer.copyright %>
<% } %>
</mdui-chip>
</div>
<div class="theme">
<mdui-chip variant="filled" class="footer-chip">
<mdui-icon name="code" slot="icon"></mdui-icon>
<a href="https://github.com/can1425/materialis" class="site-link">Materialis</a>
</mdui-chip>
</div>
<!-- 备案信息 -->
<% if (theme.footer && theme.footer.beian) { %>
<div class="beian-info">
<% if (theme.footer.beian.icp) { %>
<mdui-chip
variant="filled-tonal"
class="footer-chip beian-chip"
href="https://beian.miit.gov.cn/"
target="_blank"
>
<mdui-icon name="verified" slot="icon"></mdui-icon>
<%= theme.footer.beian.icp %>
</mdui-chip>
<% } %>
<% if (theme.footer.beian.ps) { %>
<mdui-chip
variant="filled-tonal"
class="footer-chip beian-chip"
href="http://www.beian.gov.cn/"
target="_blank"
>
<mdui-icon name="security" slot="icon"></mdui-icon>
<%= theme.footer.beian.ps %>
</mdui-chip>
<% } %>
</div>
<% } %>
<!-- 自定义徽章 -->
<% if (theme.footer && theme.footer.badges) { %>
<div class="custom-badges">
<% theme.footer.badges.forEach(function(badge) { %>
<mdui-chip
variant="<%= badge.variant || 'filled-tonal' %>"
class="footer-chip custom-badge"
<%= badge.href ? `href="${badge.href}" target="_blank"` : '' %>
>
<% if (badge.icon) { %>
<mdui-icon slot="icon" name="<%= badge.icon %>"></mdui-icon>
<% } %>
<%= badge.text %>
</mdui-chip>
<% }); %>
</div>
<% } %>
</div>
</div>
</mdui-footer>
<style>
/* 强制底部布局 */
.site-footer {
background: var(--mdui-color-surface);
border-top: 1px solid var(--mdui-color-outline-variant);
margin-top: auto !important;
width: 100%;
flex-shrink: 0;
}
.footer-content {
max-width: 1200px;
margin: 0 auto;
padding: 1.5rem 1rem;
}
/* 主要信息区域 */
.footer-main {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 1rem;
}
/* Chip 通用样式 */
.footer-chip {
--mdui-chip-height: 32px;
--mdui-chip-container-color: var(--mdui-color-surface-container);
--mdui-chip-outline-color: var(--mdui-color-outline-variant);
transition: all 0.3s ease;
font-family: var(--mdui-font-family);
font-size: 0.875rem;
line-height: 1.5;
text-align: center;
}
.footer-chip:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* 版权信息 Chip */
.copyright .footer-chip {
--mdui-chip-container-color: var(--mdui-color-primary-container);
--mdui-chip-label-text-color: var(--mdui-color-on-primary-container);
}
/* 备案信息 Chip */
.beian-chip {
--mdui-chip-container-color: var(--mdui-color-secondary-container);
--mdui-chip-label-text-color: var(--mdui-color-on-secondary-container);
}
/* 主题信息 Chip */
.theme-info .footer-chip {
--mdui-chip-outline-color: var(--mdui-color-primary);
--mdui-chip-label-text-color: var(--mdui-color-primary);
}
/* 链接样式 */
.site-link {
color: inherit;
text-decoration: none;
font-weight: 500;
margin: 0 2px;
vertical-align: middle;
}
.site-link:hover {
text-decoration: underline;
}
.tech-link {
color: inherit;
text-decoration: none;
font-weight: 500;
margin: 0 2px;
vertical-align: middle;
}
.tech-link:hover {
text-decoration: underline;
}
/* 备案信息 */
.beian-info {
display: flex;
gap: 0.5rem;
flex-shrink: 0;
align-items: center;
justify-content: center;
}
/* 自定义徽章 */
.custom-badges {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
flex-shrink: 0;
align-items: center;
justify-content: flex-end;
}
.custom-badge {
cursor: pointer;
}
/* 响应式设计 */
@media (max-width: 1024px) {
.footer-main {
justify-content: center;
gap: 1rem;
}
.custom-badges {
justify-content: center;
}
}
@media (max-width: 768px) {
.footer-content {
padding: 1rem;
}
.footer-main {
flex-direction: column;
text-align: center;
gap: 0.75rem;
}
.custom-badges {
justify-content: center;
}
.beian-info {
flex-direction: column;
align-items: center;
}
.footer-chip {
text-align: center;
justify-content: center;
}
}
@media (max-width: 480px) {
.footer-content {
padding: 0.75rem;
}
.footer-chip {
--mdui-chip-height: 28px;
font-size: 0.875rem;
}
.custom-badges,
.beian-info {
width: 100%;
}
}
</style>

View File

@ -0,0 +1,286 @@
<style>
.friends-page {
max-width: 900px;
margin: 0 auto;
padding: 40px 20px;
display: flex;
flex-direction: column;
align-items: center;
gap: 24px;
}
.friends-header {
width: 100%;
text-align: center;
padding: 40px 32px;
border-radius: 24px;
background-color: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
}
.friends-card {
width: 100%;
border-radius: 20px;
overflow: hidden;
background-color: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface));
}
.friends-list {
display: flex;
flex-direction: column;
padding: 0;
}
.friend-item {
display: flex;
align-items: center;
gap: 16px;
padding: 20px 32px;
text-decoration: none;
color: inherit;
transition: all 0.2s ease;
border-bottom: 1px solid rgb(var(--mdui-color-outline-variant));
}
.friend-item:last-child {
border-bottom: none;
}
.friend-item:hover {
background-color: rgb(var(--mdui-color-surface-container-highest));
transform: translateX(4px);
}
.friend-avatar {
flex-shrink: 0;
width: 50px;
height: 50px;
border-radius: 12px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
background-color: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
}
.friend-avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}
.friend-content {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
gap: 4px;
}
.friend-name {
font-size: 1rem;
font-weight: 500;
margin: 0;
line-height: 1.4;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.friend-desc {
font-size: 0.875rem;
margin: 0;
opacity: 0.7;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
}
.friend-arrow {
flex-shrink: 0;
color: rgb(var(--mdui-color-outline));
transition: transform 0.2s ease;
}
.friend-item:hover .friend-arrow {
transform: translateX(4px);
}
.friends-empty {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
margin-top: 40px;
}
.friends-empty-card {
width: 100%;
max-width: 500px;
padding: 60px 40px;
text-align: center;
border-radius: 24px;
display: flex;
flex-direction: column;
align-items: center;
background-color: rgb(var(--mdui-color-surface-container));
color: rgb(var(--mdui-color-on-surface));
}
.friends-empty-icon {
font-size: 80px;
margin-bottom: 24px;
opacity: 0.6;
color: rgb(var(--mdui-color-primary-container));
}
.friends-empty-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
/* 平板设备 */
@media (max-width: 1024px) {
.friends-page {
padding: 32px 20px;
gap: 20px;
}
.friends-header {
padding: 32px 24px;
}
.friend-item {
padding: 18px 24px;
}
}
/* 手机设备 */
@media (max-width: 768px) {
.friends-page {
padding: 24px 16px;
gap: 16px;
}
.friends-header {
padding: 24px 20px;
}
.friend-item {
padding: 16px 20px;
gap: 12px;
}
.friend-avatar {
width: 45px;
height: 45px;
border-radius: 10px;
}
.friends-empty-card {
padding: 40px 24px;
}
.friends-empty-icon {
font-size: 60px;
margin-bottom: 20px;
}
}
/* 小屏手机 */
@media (max-width: 480px) {
.friends-page {
padding: 20px 12px;
gap: 12px;
}
.friends-header {
padding: 20px 16px;
}
.friend-item {
padding: 14px 16px;
gap: 10px;
}
.friend-avatar {
width: 40px;
height: 40px;
border-radius: 8px;
}
.friend-name {
font-size: 0.9375rem;
}
.friend-desc {
font-size: 0.8125rem;
}
}
</style>
<div class="friends-page">
<!-- 页面标题 -->
<mdui-card class="mdui-primary friends-header">
<mdui-typography variant="h3" style="margin-bottom: 12px; font-weight: 500;">
<%= __('friend_links.title') %>
</mdui-typography>
<mdui-typography variant="h6" style="opacity: 0.8; font-weight: 400;">
<% if (site.data.links && site.data.links.length > 0) { %>
<%= __('friend_links.count_prefix') %> <mdui-badge style="margin: 0 8px;"><%= site.data.links.length %></mdui-badge> <%= __('friend_links.count_suffix') %>
<% } else { %>
<%= __('friend_links.empty') %>
<% } %>
</mdui-typography>
</mdui-card>
<!-- 友链列表 -->
<% if (site.data.links && site.data.links.length > 0) { %>
<mdui-card class="mdui-primary friends-card">
<div class="friends-list">
<% site.data.links.forEach(function(link) { %>
<a href="<%= link.url %>" class="friend-item" target="_blank" rel="noopener noreferrer">
<mdui-avatar class="friend-avatar" variant="rounded">
<% if (link.avatar) { %>
<img src="<%= link.avatar %>" alt="<%= link.name %>" loading="lazy">
<% } else { %>
<mdui-icon>person</mdui-icon>
<% } %>
</mdui-avatar>
<div class="friend-content">
<div class="friend-name"><%= link.name %></div>
<% if (link.desc) { %>
<div class="friend-desc"><%= link.desc %></div>
<% } %>
</div>
<mdui-icon class="friend-arrow">chevron_right</mdui-icon>
</a>
<% }); %>
</div>
</mdui-card>
<% } else { %>
<!-- 空状态 -->
<div class="friends-empty">
<mdui-card class="mdui-primary friends-empty-card">
<mdui-icon class="friends-empty-icon">link_off</mdui-icon>
<div class="friends-empty-content">
<mdui-typography variant="h5" style="margin-bottom: 8px; font-weight: 500;">
<%= __('friend_links.empty') %>
</mdui-typography>
<mdui-typography variant="body" style="opacity: 0.7; line-height: 1.5;">
<%= __('friend_links.empty_desc') %>
</mdui-typography>
</div>
</mdui-card>
</div>
<% } %>
</div>

View File

@ -0,0 +1,154 @@
<div id="qexo-friends"></div>
<style>
.qexo_inner,
.qexo_loader {
border-radius: 50%;
position: absolute;
}
.qexo_loading {
min-height: 200px;
}
.qexo_part {
min-height: 100px;
}
.qexo_loader {
background-color: rgba(var(--mdui-color-on-surface-variant), 0.2); /* 使用主题色并调整透明度 */
width: 64px;
height: 64px;
perspective: 800px;
}
.qexo_inner {
box-sizing: border-box;
width: 100%;
height: 100%;
}
.qexo_inner.one {
left: 0;
top: 0;
animation: rotate-one 1s linear infinite;
border-bottom: 3px solid var(--mdui-color-on-surface-variant); /* 使用主题变量 */
position: absolute;
}
.qexo_inner.two {
right: 0;
top: 0;
animation: rotate-two 1s linear infinite;
border-right: 3px solid var(--mdui-color-on-surface-variant); /* 使用主题变量 */
position: absolute;
}
.qexo_inner.three {
right: 0;
bottom: 0;
animation: rotate-three 1s linear infinite;
border-top: 3px solid var(--mdui-color-on-surface-variant); /* 使用主题变量 */
position: absolute;
}
@keyframes rotate-one {
0% {
transform: rotateX(35deg) rotateY(-45deg) rotateZ(0);
}
100% {
transform: rotateX(35deg) rotateY(-45deg) rotateZ(360deg);
}
}
@keyframes rotate-two {
0% {
transform: rotateX(50deg) rotateY(10deg) rotateZ(0);
}
100% {
transform: rotateX(50deg) rotateY(10deg) rotateZ(360deg);
}
}
@keyframes rotate-three {
0% {
transform: rotateX(35deg) rotateY(55deg) rotateZ(0);
}
100% {
transform: rotateX(35deg) rotateY(55deg) rotateZ(360deg);
}
}
.qexo-friends {
opacity: 0.9;
margin-left: 10px;
padding-bottom: 15px;
}
.qexo-friends a {
color: var(--mdui-color-on-surface); /* 使用主题文本色 */
}
.qexo-friends p {
display: none;
}
.qexo-friendurl {
text-decoration: none !important;
color: var(--mdui-color-on-surface); /* 使用主题文本色 */
}
.qexo-myfriend {
width: 56px !important;
height: 56px !important;
border-radius: 50%;
border: 1px solid var(--mdui-color-outline); /* 使用轮廓色 */
box-shadow: var(--mdui-elevation-1); /* 使用MDUI高程阴影 */
margin-top: 14px !important;
margin-left: 14px !important;
background-color: var(--mdui-color-surface) !important; /* 使用表面色 */
}
.qexo-frienddiv {
height: 92px;
margin-top: 10px;
width: calc(100% - 10px);
margin-right: 10px;
box-shadow: var(--mdui-elevation-1); /* 使用MDUI高程阴影 */
display: inline-block !important;
background-color: var(--mdui-color-surface-container-low); /* 使用表面容器色 */
border-radius: var(--mdui-shape-corner-medium); /* 使用中等圆角 */
}
@media screen and (min-device-width: 600px) {
.qexo-frienddiv {
width: calc(49% - 5px);
}
}
.qexo-frienddiv:hover {
background: rgba(var(--mdui-color-primary), 0.08); /* 使用主色并添加透明度 */
box-shadow: var(--mdui-elevation-2); /* 悬停时阴影升高 */
}
.qexo-frienddiv:hover .qexo-frienddivleft img {
transition: transform 0.9s var(--mdui-motion-easing-emphasized) !important; /* 使用MDUI强调缓动 */
transform: rotate(360deg) !important;
}
.qexo-frienddivleft {
width: 92px;
float: left;
margin-right: 4px;
}
.qexo-frienddivright {
margin-top: 18px;
margin-right: 18px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
color: var(--mdui-color-on-surface); /* 确保文本使用主题色 */
}
</style>
<script src="https://registry.npmmirror.com/qexo-static/1.6.0/files/hexo/friends.js"></script>
<script>loadQexoFriends("qexo-friends", "<%= theme.links.qexo.url %>")</script>

View File

@ -0,0 +1,70 @@
<mdui-card
class="article-card"
variant="filled"
onclick="location.href='<%= url_for(post.path) %>'">
<div class="article-content">
<!-- 封面图片 -->
<% if (post.cover) { %>
<div class="article-cover">
<img src="<%= post.cover %>" alt="<%= post.title %>" />
</div>
<% } %>
<!-- 内容区域 -->
<div class="article-main">
<mdui-typography class="article-title">
<%= post.title %>
</mdui-typography>
<!-- 有封面时摘要限制20字 -->
<% if (post.cover) { %>
<span><%- truncate(strip_html(post.content), { length: 120 }) %></span>
<% } else { %>
<span><%- truncate(strip_html(post.content), { length: 200 }) %></span>
<% } %>
<div class="article-meta">
<mdui-typography variant="body2" class="article-date">
<mdui-icon name="calendar_today" style="font-size: 1.1rem;"></mdui-icon>
<%= post.date.format('YYYY-MM-DD') %>
</mdui-typography>
<div class="article-tags">
<% if (post.tags && post.tags.length > 0) { %>
<% post.tags.data.slice(0, 2).forEach((tag) => { %>
<mdui-chip
size="small"
variant="filled"
style="background: var(--mdui-color-secondary-container); color: var(--mdui-color-on-secondary-container);">
<mdui-icon name="sell" slot="icon" style="font-size: 1rem;"></mdui-icon>
<%= tag.name %>
</mdui-chip>
<% }); %>
<% if (post.tags.length > 2) { %>
<mdui-chip
size="small"
variant="filled"
style="background: var(--mdui-color-tertiary-container); color: var(--mdui-color-on-tertiary-container);">
+<%= post.tags.length - 2 %>
</mdui-chip>
<% } %>
<% } else if (post.categories && post.categories.length > 0) { %>
<mdui-chip
size="small"
variant="filled"
style="background: var(--mdui-color-primary-container); color: var(--mdui-color-on-primary-container);">
<mdui-icon name="folder" slot="icon" style="font-size: 1rem;"></mdui-icon>
<%= post.categories.data[0].name %>
</mdui-chip>
<% } %>
</div>
</div>
</div>
<!-- 右侧:阅读更多 -->
<mdui-button class="article-side" variant="tonal">
<mdui-icon name="arrow_forward"></mdui-icon>
</mdui-button>
</div>
</mdui-card>

View File

@ -0,0 +1,203 @@
<div id="qexot"></div>
<script src="https://registry.npmmirror.com/qexo-static/1.6.0/files/hexo/talks.js"></script>
<style>
/*加载样式*/
.qexo_inner,
.qexo_loader {
border-radius: 50%;
position: absolute;
}
.qexo_loading {
min-height: 200px;
}
.qexo_part {
min-height: 100px;
}
.qexo_loader {
background-color: rgba(var(--mdui-color-on-surface-variant), 0.2);
width: 64px;
height: 64px;
perspective: 800px;
}
.qexo_inner {
box-sizing: border-box;
width: 100%;
height: 100%;
}
.qexo_inner.one {
left: 0;
top: 0;
animation: rotate-one 1s linear infinite;
border-bottom: 3px solid var(--mdui-color-primary);
position: absolute;
}
.qexo_inner.two {
right: 0;
top: 0;
animation: rotate-two 1s linear infinite;
border-right: 3px solid var(--mdui-color-primary);
position: absolute;
}
.qexo_inner.three {
right: 0;
bottom: 0;
animation: rotate-three 1s linear infinite;
border-top: 3px solid var(--mdui-color-primary);
position: absolute;
}
@keyframes rotate-one {
0% {
transform: rotateX(35deg) rotateY(-45deg) rotateZ(0);
}
100% {
transform: rotateX(35deg) rotateY(-45deg) rotateZ(360deg);
}
}
@keyframes rotate-two {
0% {
transform: rotateX(50deg) rotateY(10deg) rotateZ(0);
}
100% {
transform: rotateX(50deg) rotateY(10deg) rotateZ(360deg);
}
}
@keyframes rotate-three {
0% {
transform: rotateX(35deg) rotateY(55deg) rotateZ(0);
}
100% {
transform: rotateX(35deg) rotateY(55deg) rotateZ(360deg);
}
}
/* 每条动态 */
#qexot > section > div > div > div {
display: flex;
flex-direction: initial;
flex-wrap: wrap;
border-radius: var(--mdui-shape-corner-medium);
padding: 8px 16px;
margin-top: 12px;
}
#qexot > section > div > div {
box-shadow: var(--mdui-elevation-1);
background-color: var(--mdui-color-surface-container-low);
border-radius: var(--mdui-shape-corner-medium);
}
#qexot p {
margin: 0;
}
#qexot .datafrom i {
margin-right: 4px;
}
/* 时间 */
#qexot > section > div > div > div .qexot-datatime {
order: 1;
font-size: 0.6rem;
color: var(--mdui-color-on-surface-variant);
}
/* 标签 */
#qexot > section > div > div > div .qexot-tags {
font-size: .6rem;
margin-top: -7%;
color: var(--mdui-color-on-surface-variant);
}
/* 内容 */
#qexot > section > div > div > div .datacont {
order: 0;
font-size: 1rem;
font-weight: bold;
width: 100%;
line-height: 1.38;
margin: 8px 0;
color: var(--mdui-color-on-surface);
}
#qexot .qexot-like {
cursor: pointer;
padding: 0 10px;
font-size: xx-small;
fill: currentColor;
color: var(--mdui-color-primary);
}
#qexot .qexot-bottom {
display: flex;
justify-content: space-between;
width: 100%;
}
#qexot .qexot-top {
position: absolute;
white-space: nowrap;
right: 13%;
}
#qexot div.qexot-content {
display: flex;
flex-direction: initial;
flex-wrap: wrap;
border-radius: var(--mdui-shape-corner-medium);
width: 100%;
}
#qexot .qexot div div.qexot-item {
width: 90%;
border-radius: var(--mdui-shape-corner-medium);
padding: 1rem;
padding-bottom: 0.5rem;
transition: 0.3s;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
justify-content: space-between;
align-items: flex-start;
margin-top: 10px;
margin-bottom: 10px;
margin-left: 5%;
background-color: var(--mdui-color-surface-container-low);
}
.qexot-more {
border-width: 1px;
border: 1px solid var(--mdui-color-outline);
border-radius: var(--mdui-shape-corner-small);
box-shadow: var(--mdui-elevation-1);
background-color: var(--mdui-color-surface);
cursor: pointer;
padding-bottom: calc(0.375em - 1px);
padding-top: calc(0.375em - 1px);
text-align: center;
white-space: nowrap;
width: 20%;
margin-bottom: 10px;
color: var(--mdui-color-on-surface);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.qexot-more:hover {
background-color: var(--mdui-color-primary-container);
color: var(--mdui-color-on-primary-container);
box-shadow: var(--mdui-elevation-2);
}
</style>
<script>showQexoTalks("qexot", "<%= theme.shuoshuo.qexo.url %>}", 5)</script>

View File

@ -0,0 +1,146 @@
<%
const talks = site.data.talks && site.data.talks.talks ? site.data.talks.talks : [];
%>
<style>
.talk-card {
width: 100%;
border-radius: 16px;
overflow: hidden;
background-color: rgb(var(--mdui-color-surface-container-low));
color: rgb(var(--mdui-color-on-surface));
margin-bottom: 20px;
padding: 10px;
}
.talk-card-content {
padding: 24px;
}
.talk-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
}
.talk-avatar {
width: 50px;
height: 50px;
border-radius: 50%;
object-fit: cover;
background: rgb(var(--mdui-color-primary-container));
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.talk-avatar:hover {
transform: scale(1.05);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.talk-avatar-placeholder {
width: 40px;
height: 40px;
border-radius: 50%;
background: rgb(var(--mdui-color-primary-container));
color: rgb(var(--mdui-color-on-primary-container));
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.talk-avatar-placeholder:hover {
transform: scale(1.05);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.talk-author-name {
font-size: 16px;
font-weight: 500;
margin-bottom: 2px;
}
.talk-date {
font-size: 14px;
opacity: 0.8;
}
.talk-content {
font-size: 15px;
line-height: 1.6;
margin-bottom: 16px;
}
.talk-images {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
gap: 8px;
}
.talk-image {
width: 100%;
border-radius: 8px;
aspect-ratio: 1;
object-fit: cover;
}
.no-talks {
width: 100%;
text-align: center;
padding: 40px 24px;
border-radius: 16px;
background-color: rgb(var(--mdui-color-surface-container-low));
color: rgb(var(--mdui-color-on-surface-variant));
}
</style>
<mdui-layout-main>
<div class="articles-container">
<!-- 说说列表 -->
<% if (talks && talks.length > 0) { %>
<% talks.forEach(function(talk) { %>
<mdui-card variant="filled" class="talk-card">
<mdui-card-content class="talk-card-content">
<!-- 头部信息 -->
<div class="talk-header">
<mdui-avatar variant="circular" class="talk-avatar">
<% if (talk.avatar) { %>
<img src="<%= talk.avatar %>" alt="<%= talk.author || config.author %>" loading="lazy">
<% } else { %>
<mdui-icon>person</mdui-icon>
<% } %>
</mdui-avatar>
<div>
<div class="talk-author-name"><%= talk.author || config.author %></div>
<div class="talk-date"><%= talk.date %></div>
</div>
</div>
<!-- 内容 -->
<div class="talk-content">
<%= talk.content %>
</div>
<!-- 图片 -->
<% if (talk.images && talk.images.length > 0) { %>
<div class="talk-images">
<% talk.images.forEach(function(image) { %>
<img src="<%= image %>" alt="说说图片" class="talk-image">
<% }); %>
</div>
<% } %>
</mdui-card-content>
</mdui-card>
<% }); %>
<% } else { %>
<div class="no-talks">
<mdui-icon name="chat" style="font-size: 3rem; opacity: 0.5; margin-bottom: 16px;"></mdui-icon>
<mdui-typography variant="h6"><%= __('shuoshuo.no_talks') %></mdui-typography>
<mdui-typography variant="body2" style="margin-top: 8px;"><%= __('shuoshuo.no_talks_desc') %></mdui-typography>
</div>
<% } %>
</div>
</mdui-layout-main>

View File

@ -0,0 +1,147 @@
<mdui-dialog id="interceptDialog" class="interceptor-dialog" close-on-overlay-click>
<div class="mdui-dialog-title">即将离开本站</div>
<div class="mdui-dialog-content">
<div style="text-align: center; margin-bottom: 20px;">
<mdui-icon name="open_in_new" style="font-size: 48px; color: var(--mdui-color-primary);"></mdui-icon>
</div>
<p>您即将访问外部网站:</p>
<div class="link-info">
<strong id="interceptLinkText">链接文本</strong>
<br>
<small style="color: var(--mdui-color-on-surface-variant);" id="interceptDomain">域名</small>
</div>
<div class="mdui-typo-caption" style="margin-top: 16px; color: var(--mdui-color-on-surface-variant);">
<mdui-icon name="info" size="small"></mdui-icon>
请注意:外部网站的内容和安全性不在本站控制范围内
</div>
</div>
<div class="mdui-dialog-actions">
<mdui-button id="cancelBtn">取消</mdui-button>
<mdui-button variant="filled" id="continueBtn">继续访问</mdui-button>
</div>
</mdui-dialog>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 获取对话框元素
const interceptDialog = document.querySelector('#interceptDialog');
const continueBtn = document.querySelector('#continueBtn');
const cancelBtn = document.querySelector('#cancelBtn');
// 存储当前点击的链接
let currentLink = null;
// 图片文件后缀列表
const imageExtensions = [
'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp',
'.svg', '.ico', '.tiff', '.tif', '.avif', '.heic'
];
// 拦截所有外部链接的点击事件
document.addEventListener('click', function(e) {
let target = e.target;
// 向上查找链接元素
while (target && target.tagName !== 'A') {
target = target.parentElement;
}
if (target && target.tagName === 'A') {
const href = target.getAttribute('href');
// 只拦截外部链接,但不拦截图片链接
if (href && isExternalLink(href) && !isImageLink(href)) {
e.preventDefault();
currentLink = href;
showInterceptionDialog(href, target.textContent || target.title || '未知网站');
}
}
});
// 判断是否为外部链接
function isExternalLink(href) {
return (
href.startsWith('http://') ||
href.startsWith('https://') ||
href.startsWith('//') ||
href.startsWith('mailto:') ||
href.startsWith('tel:')
);
}
// 判断是否为图片链接
function isImageLink(href) {
const lowerHref = href.toLowerCase();
return imageExtensions.some(ext => lowerHref.endsWith(ext));
}
// 显示拦截对话框
function showInterceptionDialog(url, linkText) {
const domain = new URL(url, window.location.origin).hostname;
// 更新对话框内容
document.getElementById('interceptLinkText').textContent = linkText;
document.getElementById('interceptDomain').textContent = domain;
// 打开对话框
interceptDialog.open = true;
}
// 继续访问按钮点击事件
continueBtn.addEventListener('click', function() {
if (currentLink) {
window.open(currentLink, '_blank', 'noopener,noreferrer');
interceptDialog.open = false;
currentLink = null;
}
});
// 取消按钮点击事件
cancelBtn.addEventListener('click', function() {
interceptDialog.open = false;
currentLink = null;
});
interceptDialog.addEventListener('close', function() {
currentLink = null;
});
});
</script>
<style>
/* 对话框内容样式 */
.interceptor-dialog {
background-color: rgba(var(--mdui-color-surface), 0.95) !important;
backdrop-filter: blur(10px) !important;
-webkit-backdrop-filter: blur(10px) !important;
border-radius: 12px !important;
border: 1px solid rgba(var(--mdui-color-outline), 0.1) !important;
}
.interceptor-dialog .mdui-dialog-title {
font-weight: 600;
text-align: center;
padding: 24px 24px 0;
color: var(--mdui-color-on-surface);
}
.interceptor-dialog .mdui-dialog-content {
padding: 20px 24px;
color: var(--mdui-color-on-surface-variant);
}
.interceptor-dialog .link-info {
background: rgba(var(--mdui-color-primary), 0.08);
padding: 16px;
border-radius: 8px;
margin: 16px 0;
text-align: center;
border: 1px solid rgba(var(--mdui-color-primary), 0.2);
}
.interceptor-dialog .mdui-dialog-actions {
padding: 8px 24px 24px;
justify-content: flex-end;
gap: 8px;
}
</style>

View File

@ -0,0 +1,365 @@
<!-- 文章导航按钮 -->
<mdui-button-icon id="toc-toggle">
<mdui-icon name="menu_book"></mdui-icon>
</mdui-button-icon>
<!-- 文章导航弹窗 -->
<mdui-dialog id="toc-dialog" close-on-esc close-on-overlay-click class="toc-dialog">
<div class="toc-header">
<mdui-typography variant="h6">文章导航</mdui-typography>
<mdui-button-icon class="close-btn" id="toc-close">
<mdui-icon name="close"></mdui-icon>
</mdui-button-icon>
</div>
<div class="toc-content">
<div id="toc-container" class="toc-container">
<!-- 目录内容将通过JavaScript动态加载 -->
<div class="toc-placeholder">
<mdui-icon name="menu_book"></mdui-icon>
<div>暂无文章导航</div>
</div>
</div>
</div>
<div class="toc-footer">
<mdui-button id="toc-close-btn">关闭</mdui-button>
</div>
</mdui-dialog>
<style>
.toc-dialog::part(overlay) {
backdrop-filter: blur(10px) saturate(180%);
-webkit-backdrop-filter: blur(10px) saturate(180%); /* Safari 支持 */
background: rgba(0, 0, 0, 0.4);
}
.toc-dialog {
--mdui-dialog-width: 500px;
--mdui-dialog-height: 70vh;
--mdui-dialog-max-height: 80vh;
}
.toc-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 24px 0;
margin-bottom: 16px;
}
.toc-content {
padding: 0 24px;
flex: 1;
overflow: hidden;
}
.toc-container {
height: 100%;
overflow-y: auto;
padding: 8px 0;
}
.toc-container::-webkit-scrollbar {
width: 6px;
}
.toc-container::-webkit-scrollbar-track {
background: var(--mdui-color-surface-container);
}
.toc-container::-webkit-scrollbar-thumb {
background: var(--mdui-color-outline-variant);
border-radius: 3px;
}
.toc-placeholder {
text-align: center;
padding: 60px 20px;
color: var(--mdui-color-on-surface-variant);
}
.toc-placeholder mdui-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.5;
}
.toc-footer {
display: flex;
justify-content: flex-end;
padding: 16px 24px;
border-top: 1px solid var(--mdui-color-outline-variant);
}
.toc-list {
list-style: none;
padding-left: 0;
margin: 0;
}
.toc-list ul {
padding-left: 1.5rem;
margin: 0.5rem 0;
}
.toc-list li {
margin: 0.25rem 0;
}
.toc-link {
display: block;
padding: 0.5rem 0.75rem;
color: var(--mdui-color-on-surface);
text-decoration: none;
border-radius: 6px;
transition: all 0.2s ease;
}
.toc-link:hover {
background-color: var(--mdui-color-surface-container-hover);
color: var(--mdui-color-primary);
}
.toc-link.active {
background-color: var(--mdui-color-primary-container);
color: var(--mdui-color-on-primary-container);
font-weight: 500;
}
@media (max-width: 600px) {
.toc-dialog {
--mdui-dialog-width: 95vw;
--mdui-dialog-height: 80vh;
}
.toc-header {
padding: 16px 16px 0;
}
.toc-content {
padding: 0 16px;
}
.toc-footer {
padding: 12px 16px;
}
}
</style>
<script>
class HexoTOC {
constructor() {
this.dialog = document.getElementById('toc-dialog');
this.tocContainer = document.getElementById('toc-container');
this.init();
}
init() {
this.bindEvents();
}
bindEvents() {
const tocToggle = document.getElementById('toc-toggle');
const tocClose = document.getElementById('toc-close');
const tocCloseBtn = document.getElementById('toc-close-btn');
if (tocToggle) {
tocToggle.addEventListener('click', () => {
this.open();
});
}
if (tocClose) {
tocClose.addEventListener('click', () => {
this.close();
});
}
if (tocCloseBtn) {
tocCloseBtn.addEventListener('click', () => {
this.close();
});
}
// 监听ESC键关闭
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && this.isOpen()) {
this.close();
}
});
}
open() {
if (this.dialog) {
this.dialog.open = true;
// 加载当前页面的目录
this.loadCurrentPageTOC();
}
}
close() {
if (this.dialog) {
this.dialog.open = false;
}
}
isOpen() {
return this.dialog && this.dialog.open;
}
loadCurrentPageTOC() {
// 检查是否在文章页面
if (typeof window.page !== 'undefined' && window.page.title) {
// 从页面内容中提取标题
this.extractTOCFromContent();
} else {
// 显示占位符
this.tocContainer.innerHTML = `
<div class="toc-placeholder">
<mdui-icon name="menu_book"></mdui-icon>
<div>暂无文章导航</div>
</div>
`;
}
}
extractTOCFromContent() {
// 获取文章内容
const content = document.querySelector('.article-content');
if (!content) {
this.tocContainer.innerHTML = `
<div class="toc-placeholder">
<mdui-icon name="menu_book"></mdui-icon>
<div>暂无文章导航</div>
</div>
`;
return;
}
// 提取标题元素
const headers = content.querySelectorAll('h1, h2, h3, h4, h5, h6');
if (headers.length === 0) {
this.tocContainer.innerHTML = `
<div class="toc-placeholder">
<mdui-icon name="menu_book"></mdui-icon>
<div>暂无文章导航</div>
</div>
`;
return;
}
// 构建目录HTML
let tocHTML = '<ul class="toc-list">';
headers.forEach((header, index) => {
// 为标题添加ID如果没有
if (!header.id) {
header.id = 'toc-header-' + index;
}
const level = parseInt(header.tagName.charAt(1));
const indent = (level - 1) * 20; // 根据标题级别设置缩进
tocHTML += `
<li style="margin-left: ${indent}px;">
<a href="#${header.id}" class="toc-link" data-target="${header.id}">
${header.textContent}
</a>
</li>
`;
});
tocHTML += '</ul>';
this.tocContainer.innerHTML = tocHTML;
// 绑定导航链接事件
this.bindTOCEvents();
}
bindTOCEvents() {
const links = this.tocContainer.querySelectorAll('.toc-link');
links.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const targetId = link.getAttribute('data-target');
if (targetId) {
const target = document.getElementById(targetId);
if (target) {
// 平滑滚动到目标元素
target.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
// 关闭对话框
this.close();
}
}
});
});
// 高亮当前正在阅读的标题
this.highlightCurrentHeader();
window.addEventListener('scroll', this.throttle(() => {
this.highlightCurrentHeader();
}, 100));
}
// 节流函数,防止滚动事件过于频繁
throttle(fn, wait) {
let last = 0;
return function(...args) {
const now = Date.now();
if (now - last > wait) {
last = now;
fn.apply(this, args);
}
};
}
// 高亮当前正在阅读的标题
highlightCurrentHeader() {
const headers = Array.from(document.querySelectorAll('.article-content h1, .article-content h2, .article-content h3, .article-content h4, .article-content h5, .article-content h6'));
if (!headers.length) return;
const scrollY = window.scrollY || window.pageYOffset;
const offset = 80; // 可根据实际导航栏高度调整
let currentId = headers[0].id;
for (let i = 0; i < headers.length; i++) {
const rect = headers[i].getBoundingClientRect();
const top = rect.top + scrollY - offset;
if (scrollY >= top) {
currentId = headers[i].id;
} else {
break;
}
}
// 移除所有高亮
this.tocContainer.querySelectorAll('.toc-link').forEach(link => {
link.classList.remove('active');
});
// 高亮当前
const activeLink = this.tocContainer.querySelector('.toc-link[data-target="' + currentId + '"]');
if (activeLink) {
activeLink.classList.add('active');
// 滚动目录到可见区域
const tocScroll = this.tocContainer;
const linkRect = activeLink.getBoundingClientRect();
const tocRect = tocScroll.getBoundingClientRect();
if (linkRect.top < tocRect.top || linkRect.bottom > tocRect.bottom) {
tocScroll.scrollTop = activeLink.offsetTop - tocScroll.clientHeight / 2 + activeLink.clientHeight / 2;
}
}
}
}
// 初始化
document.addEventListener('DOMContentLoaded', () => {
window.hexoTOC = new HexoTOC();
});
// 页面加载完成后再次初始化,确保能获取到文章内容
window.addEventListener('load', () => {
if (!window.hexoTOC) {
window.hexoTOC = new HexoTOC();
}
});
</script>

View File

@ -0,0 +1,384 @@
<!-- 搜索按钮 -->
<mdui-button-icon id="search-toggle">
<mdui-icon name="search"></mdui-icon>
</mdui-button-icon>
<!-- 搜索弹窗 -->
<mdui-dialog id="search-dialog" close-on-esc close-on-overlay-click class="search-dialog">
<div class="search-header">
<mdui-text-field class="search-input" placeholder="搜索文章、标签、分类..." id="search-input">
<mdui-icon name="search" slot="icon"></mdui-icon>
</mdui-text-field>
<mdui-button-icon class="close-btn" id="search-close">
<mdui-icon name="close"></mdui-icon>
</mdui-button-icon>
</div>
<div class="search-content">
<div id="search-results" class="search-results">
<div class="search-placeholder">
<mdui-icon name="search"></mdui-icon>
<div>输入关键词开始搜索</div>
</div>
</div>
</div>
<div class="search-footer">
<span class="result-count" id="result-count"></span>
<mdui-button id="search-close-btn">关闭</mdui-button>
</div>
</mdui-dialog>
<style>
.search-dialog::part(overlay) {
backdrop-filter: blur(10px) saturate(180%);
-webkit-backdrop-filter: blur(10px) saturate(180%); /* Safari 支持 */
background: rgba(0, 0, 0, 0.4);
}
.search-dialog {
--mdui-dialog-width: 600px;
--mdui-dialog-height: 70vh;
}
.search-header {
display: flex;
align-items: center;
gap: 12px;
padding: 20px 24px 0;
margin-bottom: 16px;
}
.search-input {
flex: 1;
}
.close-btn {
margin-left: auto;
}
.search-content {
padding: 0 24px;
flex: 1;
overflow: hidden;
}
.search-results {
height: 100%;
overflow-y: auto;
}
.search-results::-webkit-scrollbar {
width: 6px;
}
.search-results::-webkit-scrollbar-track {
background: var(--mdui-color-surface-container);
}
.search-results::-webkit-scrollbar-thumb {
background: var(--mdui-color-outline-variant);
border-radius: 3px;
}
.search-placeholder {
text-align: center;
padding: 60px 20px;
color: var(--mdui-color-on-surface-variant);
}
.search-placeholder mdui-icon {
font-size: 48px;
margin-bottom: 16px;
opacity: 0.5;
}
.search-footer {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 24px;
border-top: 1px solid var(--mdui-color-outline-variant);
}
.result-count {
font-size: 0.875rem;
color: var(--mdui-color-on-surface-variant);
}
.search-result-item {
padding: 16px;
border-radius: 12px;
margin-bottom: 8px;
border: 1px solid var(--mdui-color-outline-variant);
cursor: pointer;
transition: all 0.3s ease;
background: var(--mdui-color-surface);
}
.search-result-item:hover {
background: var(--mdui-color-surface-container-hover);
border-color: var(--mdui-color-primary);
transform: translateY(-2px);
}
.search-result-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: var(--mdui-color-on-surface);
margin-bottom: 8px;
}
.search-result-content {
color: var(--mdui-color-on-surface-variant);
font-size: 0.875rem;
line-height: 1.4;
margin-bottom: 8px;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.search-result-meta {
display: flex;
gap: 12px;
font-size: 0.75rem;
color: var(--mdui-color-on-surface-variant);
}
.search-highlight {
background: var(--mdui-color-primary-container);
color: var(--mdui-color-on-primary-container);
padding: 1px 4px;
border-radius: 4px;
font-weight: 600;
}
.no-results, .search-loading {
text-align: center;
padding: 60px 20px;
color: var(--mdui-color-on-surface-variant);
}
@media (max-width: 600px) {
.search-dialog {
--mdui-dialog-width: 95vw;
--mdui-dialog-height: 80vh;
}
.search-header {
padding: 16px 16px 0;
}
.search-content {
padding: 0 16px;
}
.search-footer {
padding: 12px 16px;
}
}
</style>
<script>
class HexoSearch {
constructor() {
this.searchData = null;
this.dialog = document.getElementById('search-dialog');
this.init();
}
async init() {
this.bindEvents();
this.preloadSearchData();
}
async preloadSearchData() {
try {
const response = await fetch('<%- url_for("/search.json") %>');
this.searchData = await response.json();
} catch (error) {
console.error('搜索数据加载失败:', error);
}
}
bindEvents() {
const searchToggle = document.getElementById('search-toggle');
const searchInput = document.getElementById('search-input');
const searchClose = document.getElementById('search-close');
const searchCloseBtn = document.getElementById('search-close-btn');
searchToggle.addEventListener('click', () => {
this.dialog.open = true;
setTimeout(() => searchInput.focus(), 100);
});
const closeDialog = () => {
this.dialog.open = false;
searchInput.value = '';
this.showPlaceholder();
};
searchClose.addEventListener('click', closeDialog);
searchCloseBtn.addEventListener('click', closeDialog);
searchInput.addEventListener('input', this.debounce((e) => {
this.performSearch(e.target.value);
}, 300));
// 键盘快捷键
document.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
searchToggle.click();
}
});
}
debounce(func, wait) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func(...args), wait);
};
}
performSearch(query) {
if (!query.trim()) {
this.showPlaceholder();
return;
}
if (!this.searchData) {
this.showError('搜索数据加载中...');
return;
}
const results = this.searchPosts(query);
this.displayResults(results, query);
}
searchPosts(query) {
const keywords = query.toLowerCase().split(' ').filter(k => k);
const allItems = [...(this.searchData.posts || []), ...(this.searchData.pages || [])];
return allItems
.map(item => {
let score = 0;
const title = item.title || '';
const content = item.content || '';
keywords.forEach(keyword => {
if (title.toLowerCase().includes(keyword)) score += 10;
if (content.toLowerCase().includes(keyword)) score += 2;
if (item.tags?.some(tag => tag.toLowerCase().includes(keyword))) score += 5;
if (item.categories?.some(cat => cat.toLowerCase().includes(keyword))) score += 3;
});
return { ...item, score };
})
.filter(item => item.score > 0)
.sort((a, b) => b.score - a.score)
.slice(0, 20);
}
displayResults(results, query) {
const container = document.getElementById('search-results');
const countElement = document.getElementById('result-count');
if (results.length === 0) {
container.innerHTML = `
<div class="no-results">
<mdui-icon name="search_off" style="font-size: 48px; margin-bottom: 16px;"></mdui-icon>
<div>没有找到包含 "${this.escapeHtml(query)}" 的内容</div>
</div>
`;
countElement.textContent = '';
return;
}
container.innerHTML = results.map(item => this.createResultItem(item, query)).join('');
countElement.textContent = `找到 ${results.length} 个结果`;
container.querySelectorAll('.search-result-item').forEach((item, index) => {
item.addEventListener('click', () => {
window.location.href = results[index].url;
this.dialog.open = false;
});
});
}
createResultItem(item, query) {
const title = item.title || '无标题';
const content = item.content || '';
const highlightedTitle = this.highlightText(title, query);
const excerpt = this.createExcerpt(content, query);
return `
<div class="search-result-item">
<div class="search-result-title">
<mdui-icon name="article" style="font-size: 16px;"></mdui-icon>
${highlightedTitle}
</div>
<div class="search-result-content">${excerpt}</div>
<div class="search-result-meta">
${item.date ? `<span>${item.date}</span>` : ''}
</div>
</div>
`;
}
highlightText(text, query) {
let highlighted = this.escapeHtml(text);
query.toLowerCase().split(' ').filter(k => k).forEach(keyword => {
const regex = new RegExp(`(${this.escapeRegex(keyword)})`, 'gi');
highlighted = highlighted.replace(regex, '<span class="search-highlight">$1</span>');
});
return highlighted;
}
createExcerpt(content, query) {
const keywords = query.toLowerCase().split(' ').filter(k => k);
let excerpt = this.escapeHtml(content.substring(0, 120));
keywords.forEach(keyword => {
const regex = new RegExp(`(${this.escapeRegex(keyword)})`, 'gi');
excerpt = excerpt.replace(regex, '<span class="search-highlight">$1</span>');
});
return excerpt + (content.length > 120 ? '...' : '');
}
escapeRegex(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
escapeHtml(unsafe) {
return unsafe.replace(/[&<>"']/g, m => ({
'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;'
}[m]));
}
showPlaceholder() {
document.getElementById('search-results').innerHTML = `
<div class="search-placeholder">
<mdui-icon name="search"></mdui-icon>
<div>输入关键词开始搜索</div>
</div>
`;
document.getElementById('result-count').textContent = '';
}
showError(message) {
document.getElementById('search-results').innerHTML = `
<div class="no-results">${message}</div>
`;
}
}
document.addEventListener('DOMContentLoaded', () => new HexoSearch());
</script>

View File

@ -0,0 +1,29 @@
<!-- 复制URL图标组件 -->
<mdui-button-icon class="copy-url-btn">
<mdui-icon>link</mdui-icon>
</mdui-button-icon>
<script>
document.addEventListener('DOMContentLoaded', function() {
const copyUrlBtn = document.querySelector('.copy-url-btn');
copyUrlBtn.addEventListener('click', async function() {
try {
const currentUrl = window.location.href;
await navigator.clipboard.writeText(currentUrl);
mdui.snackbar({
message: "<%= __('share.copy_success') %>",
placement: 'bottom',
timeout: 2000
});
} catch (err) {
mdui.snackbar({
message: "<%= __('share.copy_fail') %>",
placement: 'bottom',
timeout: 2000
});
}
});
});
</script>

View File

@ -0,0 +1,40 @@
<mdui-navigation-drawer class="sidebar-content" close-on-overlay-click no-animation>
<!-- 用户信息卡片 -->
<% if (theme.sidebar && (theme.sidebar.avatar || theme.sidebar.name || theme.sidebar.slogen)) { %>
<div class="sidebar-user-card">
<% if (theme.sidebar.avatar) { %>
<mdui-avatar class="sidebar-user-avatar" src="<%= theme.sidebar.avatar %>"></mdui-avatar>
<% } %>
<% if (theme.sidebar.name) { %>
<div class="sidebar-user-name"><%= theme.sidebar.name %></div>
<% } %>
<% if (theme.sidebar.slogen) { %>
<div class="sidebar-user-slogen"><%= theme.sidebar.slogen %></div>
<% } %>
</div>
<% } %>
<!-- 导航菜单 -->
<mdui-list class="nav-list">
<% if (theme.nav && theme.nav.length > 0) { %>
<% theme.nav.forEach(function(item) { %>
<mdui-list-item
class="nav-item"
href="<%= url_for(item.url) %>"
rounded
<% if (page.path.startsWith(item.url.replace(/^\//, '')) || (item.url === '/' && page.path === 'index.html')) { %>
active
<% } else { %>
variant="filled"
<% } %>
>
<mdui-icon slot="icon" name="<%= item.icon %>"></mdui-icon>
<%= item.name %>
</mdui-list-item>
<% }) %>
<% } %>
</mdui-list>
</mdui-navigation-drawer>

View File

@ -0,0 +1,28 @@
{
"name": "hexo-theme-materialis",
"version": "1.2.0",
"description": "A Material Design theme for Hexo",
"main": "index.js",
"scripts": {
"test": "echo 'No tests specified' && exit 0",
"build": "echo 'No build process needed for this theme'",
"version": "echo 'Version update completed'"
},
"keywords": [
"hexo",
"theme",
"material-design"
],
"author": "Can1425 system:error",
"license": "AGPL-3.0-or-later",
"repository": {
"type": "git",
"url": "https://github.com/Can1425/Hexo-Theme-Materialis.git"
},
"publishConfig": {
"access": "public"
},
"files": [
"**/*"
]
}

View File

@ -0,0 +1,12 @@
// scripts/404-generator.js
hexo.extend.generator.register('404', function(locals) {
return {
path: '404.html',
layout: ['404'],
data: {
title: '页面未找到 - 404',
type: '404',
layout: '404'
}
};
});

View File

@ -0,0 +1,10 @@
'use strict';
console.log(`
`);

View File

@ -0,0 +1,8 @@
hexo.extend.helper.register('get_path', function() {
if (!this.page) return '/';
if (this.page.__post) return '/post/';
if (this.page.__index) return this.config.root || '/';
if (this.page.archive) return '/archives/';
if (this.page.category) return '/category';
if (this.page.tag) return '/tags/';
})

View File

@ -0,0 +1,11 @@
hexo.extend.helper.register('sort_posts_by_date', function(posts) {
return posts.sort((a, b) => {
return new Date(b.date) - new Date(a.date);
});
});
hexo.extend.helper.register('get_latest_posts', function(posts, limit) {
return posts.sort((a, b) => {
return new Date(b.date) - new Date(a.date);
}).limit(limit);
});

View File

@ -0,0 +1,48 @@
// 简化版搜索数据生成器
hexo.extend.generator.register('search_index', function(locals) {
// 处理文章数据
const posts = [];
locals.posts.forEach(post => {
try {
posts.push({
title: post.title || '无标题',
content: (post.content || '').replace(/<[^>]+>/g, '').substring(0, 1000),
url: post.permalink || '#',
date: post.date ? post.date.format('YYYY-MM-DD') : '',
tags: post.tags ? post.tags.map(tag => tag.name).filter(Boolean) : [],
categories: post.categories ? post.categories.map(cat => cat.name).filter(Boolean) : []
});
} catch (error) {
console.log('处理文章失败:', post.title, error);
}
});
// 处理页面数据
const pages = [];
locals.pages.forEach(page => {
try {
pages.push({
title: page.title || '无标题',
content: (page.content || '').replace(/<[^>]+>/g, '').substring(0, 1000),
url: page.permalink || '#',
date: page.date ? page.date.format('YYYY-MM-DD') : ''
});
} catch (error) {
console.log('处理页面失败:', page.title, error);
}
});
const searchData = {
posts: posts,
pages: pages,
meta: {
generated: new Date().toISOString(),
total: posts.length + pages.length
}
};
return {
path: 'search.json',
data: JSON.stringify(searchData)
};
});

View File

@ -0,0 +1,300 @@
.post-card-title {
background-color: rgb(var(--mdui-color-surface-container-low));
}
.post-card-title-text {
color: rgb(var(--mdui-color-on-surface-container-low));
}
.article-title {
color: rgb(var(--mdui-color-primary));
}
.article-card {
background-color: rgb(var(--mdui-color-surface-container-low));
}
.banner-title {
background-color: rgb(var(--mdui-color-primary));
}
.banner-title-text1 {
color: rgb(var(--mdui-color-on-primary));
}
.banner-title-text2 {
color: rgb(var(--mdui-color-on-primary));
}
.post-card-wide {
background-color: rgb(var(--mdui-color-surface-container-low));
}
.article-content {
color: rgb(var(--mdui-color-on-surface));
}
/* 添加暗色模式下的样式 */
@media (prefers-color-scheme: dark) {
:root {
--mdui-color-primary-light: 143, 76, 56;
--mdui-color-surface-tint-light: 143, 76, 56;
--mdui-color-on-primary-light: 255, 255, 255;
--mdui-color-primary-container-light: 255, 219, 209;
--mdui-color-on-primary-container-light: 114, 53, 35;
--mdui-color-secondary-light: 119, 87, 78;
--mdui-color-on-secondary-light: 255, 255, 255;
--mdui-color-secondary-container-light: 255, 219, 209;
--mdui-color-on-secondary-container-light: 93, 64, 55;
--mdui-color-tertiary-light: 108, 93, 47;
--mdui-color-on-tertiary-light: 255, 255, 255;
--mdui-color-tertiary-container-light: 245, 225, 167;
--mdui-color-on-tertiary-container-light: 83, 70, 25;
--mdui-color-error-light: 186, 26, 26;
--mdui-color-on-error-light: 255, 255, 255;
--mdui-color-error-container-light: 255, 218, 214;
--mdui-color-on-error-container-light: 147, 0, 10;
--mdui-color-background-light: 255, 248, 246;
--mdui-color-on-background-light: 35, 25, 23;
--mdui-color-surface-light: 255, 248, 246;
--mdui-color-on-surface-light: 35, 25, 23;
--mdui-color-surface-variant-light: 245, 222, 216;
--mdui-color-on-surface-variant-light: 83, 67, 63;
--mdui-color-outline-light: 133, 115, 110;
--mdui-color-outline-variant-light: 216, 194, 188;
--mdui-color-shadow-light: 0, 0, 0;
--mdui-color-scrim-light: 0, 0, 0;
--mdui-color-inverse-surface-light: 57, 46, 43;
--mdui-color-inverse-on-surface-light: 255, 237, 232;
--mdui-color-inverse-primary-light: 255, 181, 160;
--mdui-color-primary-fixed-light: 255, 219, 209;
--mdui-color-on-primary-fixed-light: 58, 11, 1;
--mdui-color-primary-fixed-dim-light: 255, 181, 160;
--mdui-color-on-primary-fixed-variant-light: 114, 53, 35;
--mdui-color-secondary-fixed-light: 255, 219, 209;
--mdui-color-on-secondary-fixed-light: 44, 21, 15;
--mdui-color-secondary-fixed-dim-light: 231, 189, 178;
--mdui-color-on-secondary-fixed-variant-light: 93, 64, 55;
--mdui-color-tertiary-fixed-light: 245, 225, 167;
--mdui-color-on-tertiary-fixed-light: 35, 27, 0;
--mdui-color-tertiary-fixed-dim-light: 216, 197, 141;
--mdui-color-on-tertiary-fixed-variant-light: 83, 70, 25;
--mdui-color-surface-dim-light: 232, 214, 210;
--mdui-color-surface-bright-light: 255, 248, 246;
--mdui-color-surface-container-lowest-light: 255, 255, 255;
--mdui-color-surface-container-low-light: 255, 241, 237;
--mdui-color-surface-container-light: 252, 234, 229;
--mdui-color-surface-container-high-light: 247, 228, 224;
--mdui-color-surface-container-highest-light: 241, 223, 218;
}
/* 暗色模式下的颜色变量 */
.mdui-theme-dark {
--mdui-color-primary: var(--mdui-color-primary-dark);
--mdui-color-surface-tint: var(--mdui-color-surface-tint-dark);
--mdui-color-on-primary: var(--mdui-color-on-primary-dark);
--mdui-color-primary-container: var(--mdui-color-primary-container-dark);
--mdui-color-on-primary-container: var(--mdui-color-on-primary-container-dark);
--mdui-color-secondary: var(--mdui-color-secondary-dark);
--mdui-color-on-secondary: var(--mdui-color-on-secondary-dark);
--mdui-color-secondary-container: var(--mdui-color-secondary-container-dark);
--mdui-color-on-secondary-container: var(--mdui-color-on-secondary-container-dark);
--mdui-color-tertiary: var(--mdui-color-tertiary-dark);
--mdui-color-on-tertiary: var(--mdui-color-on-tertiary-dark);
--mdui-color-tertiary-container: var(--mdui-color-tertiary-container-dark);
--mdui-color-on-tertiary-container: var(--mdui-color-on-tertiary-container-dark);
--mdui-color-error: var(--mdui-color-error-dark);
--mdui-color-on-error: var(--mdui-color-on-error-dark);
--mdui-color-error-container: var(--mdui-color-error-container-dark);
--mdui-color-on-error-container: var(--mdui-color-on-error-container-dark);
--mdui-color-background: var(--mdui-color-background-dark);
--mdui-color-on-background: var(--mdui-color-on-background-dark);
--mdui-color-surface: var(--mdui-color-surface-dark);
--mdui-color-on-surface: var(--mdui-color-on-surface-dark);
--mdui-color-surface-variant: var(--mdui-color-surface-variant-dark);
--mdui-color-on-surface-variant: var(--mdui-color-on-surface-variant-dark);
--mdui-color-outline: var(--mdui-color-outline-dark);
--mdui-color-outline-variant: var(--mdui-color-outline-variant-dark);
--mdui-color-shadow: var(--mdui-color-shadow-dark);
--mdui-color-scrim: var(--mdui-color-scrim-dark);
--mdui-color-inverse-surface: var(--mdui-color-inverse-surface-dark);
--mdui-color-inverse-on-surface: var(--mdui-color-inverse-on-surface-dark);
--mdui-color-inverse-primary: var(--mdui-color-inverse-primary-dark);
--mdui-color-primary-fixed: var(--mdui-color-primary-fixed-dark);
--mdui-color-on-primary-fixed: var(--mdui-color-on-primary-fixed-dark);
--mdui-color-primary-fixed-dim: var(--mdui-color-primary-fixed-dim-dark);
--mdui-color-on-primary-fixed-variant: var(--mdui-color-on-primary-fixed-variant-dark);
--mdui-color-secondary-fixed: var(--mdui-color-secondary-fixed-dark);
--mdui-color-on-secondary-fixed: var(--mdui-color-on-secondary-fixed-dark);
--mdui-color-secondary-fixed-dim: var(--mdui-color-secondary-fixed-dim-dark);
--mdui-color-on-secondary-fixed-variant: var(--mdui-color-on-secondary-fixed-variant-dark);
--mdui-color-tertiary-fixed: var(--mdui-color-tertiary-fixed-dark);
--mdui-color-on-tertiary-fixed: var(--mdui-color-on-tertiary-fixed-dark);
--mdui-color-tertiary-fixed-dim: var(--mdui-color-tertiary-fixed-dim-dark);
--mdui-color-on-tertiary-fixed-variant: var(--mdui-color-on-tertiary-fixed-variant-dark);
--mdui-color-surface-dim: var(--mdui-color-surface-dim-dark);
--mdui-color-surface-bright: var(--mdui-color-surface-bright-dark);
--mdui-color-surface-container-lowest: var(--mdui-color-surface-container-lowest-dark);
--mdui-color-surface-container-low: var(--mdui-color-surface-container-low-dark);
--mdui-color-surface-container: var(--mdui-color-surface-container-dark);
--mdui-color-surface-container-high: var(--mdui-color-surface-container-high-dark);
--mdui-color-surface-container-highest: var(--mdui-color-surface-container-highest-dark);
}
}
/* 明亮模式下的颜色变量 */
@media (prefers-color-scheme: light) {
:root {
--mdui-color-primary: var(--mdui-color-primary-light);
--mdui-color-surface-tint: var(--mdui-color-surface-tint-light);
--mdui-color-on-primary: var(--mdui-color-on-primary-light);
--mdui-color-primary-container: var(--mdui-color-primary-container-light);
--mdui-color-on-primary-container: var(--mdui-color-on-primary-container-light);
--mdui-color-secondary: var(--mdui-color-secondary-light);
--mdui-color-on-secondary: var(--mdui-color-on-secondary-light);
--mdui-color-secondary-container: var(--mdui-color-secondary-container-light);
--mdui-color-on-secondary-container: var(--mdui-color-on-secondary-container-light);
--mdui-color-tertiary: var(--mdui-color-tertiary-light);
--mdui-color-on-tertiary: var(--mdui-color-on-tertiary-light);
--mdui-color-tertiary-container: var(--mdui-color-tertiary-container-light);
--mdui-color-on-tertiary-container: var(--mdui-color-on-tertiary-container-light);
--mdui-color-error: var(--mdui-color-error-light);
--mdui-color-on-error: var(--mdui-color-on-error-light);
--mdui-color-error-container: var(--mdui-color-error-container-light);
--mdui-color-on-error-container: var(--mdui-color-on-error-container-light);
--mdui-color-background: var(--mdui-color-background-light);
--mdui-color-on-background: var(--mdui-color-on-background-light);
--mdui-color-surface: var(--mdui-color-surface-light);
--mdui-color-on-surface: var(--mdui-color-on-surface-light);
--mdui-color-surface-variant: var(--mdui-color-surface-variant-light);
--mdui-color-on-surface-variant: var(--mdui-color-on-surface-variant-light);
--mdui-color-outline: var(--mdui-color-outline-light);
--mdui-color-outline-variant: var(--mdui-color-outline-variant-light);
--mdui-color-shadow: var(--mdui-color-shadow-light);
--mdui-color-scrim: var(--mdui-color-scrim-light);
--mdui-color-inverse-surface: var(--mdui-color-inverse-surface-light);
--mdui-color-inverse-on-surface: var(--mdui-color-inverse-on-surface-light);
--mdui-color-inverse-primary: var(--mdui-color-inverse-primary-light);
--mdui-color-primary-fixed: var(--mdui-color-primary-fixed-light);
--mdui-color-on-primary-fixed: var(--mdui-color-on-primary-fixed-light);
--mdui-color-primary-fixed-dim: var(--mdui-color-primary-fixed-dim-light);
--mdui-color-on-primary-fixed-variant: var(--mdui-color-on-primary-fixed-variant-light);
--mdui-color-secondary-fixed: var(--mdui-color-secondary-fixed-light);
--mdui-color-on-secondary-fixed: var(--mdui-color-on-secondary-fixed-light);
--mdui-color-secondary-fixed-dim: var(--mdui-color-secondary-fixed-dim-light);
--mdui-color-on-secondary-fixed-variant: var(--mdui-color-on-secondary-fixed-variant-light);
--mdui-color-tertiary-fixed: var(--mdui-color-tertiary-fixed-light);
--mdui-color-on-tertiary-fixed: var(--mdui-color-on-tertiary-fixed-light);
--mdui-color-tertiary-fixed-dim: var(--mdui-color-tertiary-fixed-dim-light);
--mdui-color-on-tertiary-fixed-variant: var(--mdui-color-on-tertiary-fixed-variant-light);
--mdui-color-surface-dim: var(--mdui-color-surface-dim-light);
--mdui-color-surface-bright: var(--mdui-color-surface-bright-light);
--mdui-color-surface-container-lowest: var(--mdui-color-surface-container-lowest-light);
--mdui-color-surface-container-low: var(--mdui-color-surface-container-low-light);
--mdui-color-surface-container: var(--mdui-color-surface-container-light);
--mdui-color-surface-container-high: var(--mdui-color-surface-container-high-light);
--mdui-color-surface-container-highest: var(--mdui-color-surface-container-highest-light);
}
}
:root {
--mdui-color-primary-light: 143, 76, 56;
--mdui-color-surface-tint-light: 143, 76, 56;
--mdui-color-on-primary-light: 255, 255, 255;
--mdui-color-primary-container-light: 255, 219, 209;
--mdui-color-on-primary-container-light: 114, 53, 35;
--mdui-color-secondary-light: 119, 87, 78;
--mdui-color-on-secondary-light: 255, 255, 255;
--mdui-color-secondary-container-light: 255, 219, 209;
--mdui-color-on-secondary-container-light: 93, 64, 55;
--mdui-color-tertiary-light: 108, 93, 47;
--mdui-color-on-tertiary-light: 255, 255, 255;
--mdui-color-tertiary-container-light: 245, 225, 167;
--mdui-color-on-tertiary-container-light: 83, 70, 25;
--mdui-color-error-light: 186, 26, 26;
--mdui-color-on-error-light: 255, 255, 255;
--mdui-color-error-container-light: 255, 218, 214;
--mdui-color-on-error-container-light: 147, 0, 10;
--mdui-color-background-light: 255, 248, 246;
--mdui-color-on-background-light: 35, 25, 23;
--mdui-color-surface-light: 255, 248, 246;
--mdui-color-on-surface-light: 35, 25, 23;
--mdui-color-surface-variant-light: 245, 222, 216;
--mdui-color-on-surface-variant-light: 83, 67, 63;
--mdui-color-outline-light: 133, 115, 110;
--mdui-color-outline-variant-light: 216, 194, 188;
--mdui-color-shadow-light: 0, 0, 0;
--mdui-color-scrim-light: 0, 0, 0;
--mdui-color-inverse-surface-light: 57, 46, 43;
--mdui-color-inverse-on-surface-light: 255, 237, 232;
--mdui-color-inverse-primary-light: 255, 181, 160;
--mdui-color-primary-fixed-light: 255, 219, 209;
--mdui-color-on-primary-fixed-light: 58, 11, 1;
--mdui-color-primary-fixed-dim-light: 255, 181, 160;
--mdui-color-on-primary-fixed-variant-light: 114, 53, 35;
--mdui-color-secondary-fixed-light: 255, 219, 209;
--mdui-color-on-secondary-fixed-light: 44, 21, 15;
--mdui-color-secondary-fixed-dim-light: 231, 189, 178;
--mdui-color-on-secondary-fixed-variant-light: 93, 64, 55;
--mdui-color-tertiary-fixed-light: 245, 225, 167;
--mdui-color-on-tertiary-fixed-light: 35, 27, 0;
--mdui-color-tertiary-fixed-dim-light: 216, 197, 141;
--mdui-color-on-tertiary-fixed-variant-light: 83, 70, 25;
--mdui-color-surface-dim-light: 232, 214, 210;
--mdui-color-surface-bright-light: 255, 248, 246;
--mdui-color-surface-container-lowest-light: 255, 255, 255;
--mdui-color-surface-container-low-light: 255, 241, 237;
--mdui-color-surface-container-light: 252, 234, 229;
--mdui-color-surface-container-high-light: 247, 228, 224;
--mdui-color-surface-container-highest-light: 241, 223, 218;
--mdui-color-primary-dark: 255, 181, 160;
--mdui-color-surface-tint-dark: 255, 181, 160;
--mdui-color-on-primary-dark: 86, 31, 15;
--mdui-color-primary-container-dark: 114, 53, 35;
--mdui-color-on-primary-container-dark: 255, 219, 209;
--mdui-color-secondary-dark: 231, 189, 178;
--mdui-color-on-secondary-dark: 68, 42, 34;
--mdui-color-secondary-container-dark: 93, 64, 55;
--mdui-color-on-secondary-container-dark: 255, 219, 209;
--mdui-color-tertiary-dark: 216, 197, 141;
--mdui-color-on-tertiary-dark: 59, 47, 5;
--mdui-color-tertiary-container-dark: 83, 70, 25;
--mdui-color-on-tertiary-container-dark: 245, 225, 167;
--mdui-color-error-dark: 255, 180, 171;
--mdui-color-on-error-dark: 95, 15, 12;
--mdui-color-error-container-dark: 147, 0, 10;
--mdui-color-on-error-container-dark: 255, 180, 171;
--mdui-color-background-dark: 21, 17, 16;
--mdui-color-on-background-dark: 235, 223, 219;
--mdui-color-surface-dark: 21, 17, 16;
--mdui-color-on-surface-dark: 235, 223, 219;
--mdui-color-surface-variant-dark: 83, 67, 63;
--mdui-color-on-surface-variant-dark: 216, 194, 188;
--mdui-color-outline-dark: 160, 141, 136;
--mdui-color-outline-variant-dark: 83, 67, 63;
--mdui-color-shadow-dark: 0, 0, 0;
--mdui-color-scrim-dark: 0, 0, 0;
--mdui-color-inverse-surface-dark: 235, 223, 219;
--mdui-color-inverse-on-surface-dark: 57, 46, 43;
--mdui-color-inverse-primary-dark: 143, 76, 56;
--mdui-color-primary-fixed-dark: 255, 219, 209;
--mdui-color-on-primary-fixed-dark: 58, 11, 1;
--mdui-color-primary-fixed-dim-dark: 231, 188, 176;
--mdui-color-on-primary-fixed-variant-dark: 114, 53, 35;
--mdui-color-secondary-fixed-dark: 255, 219, 209;
--mdui-color-on-secondary-fixed-dark: 44, 21, 15;
--mdui-color-secondary-fixed-dim-dark: 203, 184, 174;
--mdui-color-on-secondary-fixed-variant-dark: 93, 64, 55;
--mdui-color-tertiary-fixed-dark: 245, 225, 167;
--mdui-color-on-tertiary-fixed-dark: 35, 27, 0;
--mdui-color-tertiary-fixed-dim-dark: 188, 169, 113;
--mdui-color-on-tertiary-fixed-variant-dark: 83, 70, 25;
--mdui-color-surface-dim-dark: 21, 17, 16;
--mdui-color-surface-bright-dark: 69, 59, 56;
--mdui-color-surface-container-lowest-dark: 16, 12, 10;
--mdui-color-surface-container-low-dark: 29, 24, 22;
--mdui-color-surface-container-dark: 35, 29, 27;
--mdui-color-surface-container-high-dark: 46, 40, 37;
--mdui-color-surface-container-highest-dark: 57, 49, 46;
}

View File

@ -0,0 +1,2 @@
.articles-container
max-width: 70rem

View File

@ -0,0 +1,21 @@
@import "layout/*"
@import "widgets/*"
@import "widgets/index/*"
body
font-family: 'lxwk', 'dingtalk', 'pingfang','MiSans VF',sans-serif
html,
body
margin: 0
html,
body
margin: 0
.main
border-radius: var(--mdui-shape-corner-large)
@media screen and (min-width: var(--mdui-breakpoint-md))
.sidebar-content
open: true

View File

@ -0,0 +1,51 @@
.banner-container {
margin: 0 auto 2rem auto;
padding: 0 1rem;
display: flex;
gap: 0.75rem;
max-width: 70rem;
}
.banner-title {
width: 100%;
padding: 2rem 2rem 5rem 2rem;
font-family: 'MiSans VF', sans-serif;
}
.banner-title-text1 {
margin: 0.5rem 0;
line-height: var(--mdui-typescale-display-small-line-height);
font-size: var(--mdui-typescale-display-small-size);
letter-spacing: var(--mdui-typescale-display-small-tracking);
font-weight: var(--mdui-typescale-display-small-weight);
}
.banner-title-text2 {
margin: 0.5rem 0;
line-height: var(--mdui-typescale-body-large-line-height);
font-size: var(--mdui-typescale-body-large-size);
letter-spacing: var(--mdui-typescale-body-large-tracking);
font-weight: var(--mdui-typescale-body-large-weight);
}
.banner-sentence {
width: 100%;
}
.banner-image {
width: 30%;
height: 200px;
background: none;
position: relative;
overflow: hidden;
z-index: 0;
}
@media (max-width: 500px) {
.banner-container {
margin: 0;
}
.banner-image {
display: none;
}
}

View File

@ -0,0 +1,132 @@
.banner-container
margin: 0 auto 2rem auto
padding: 0 1rem
display: flex
gap: 0.75rem
max-width: 70rem
.banner-title
width: 100%
padding: 2rem 2rem 5rem 2rem
min-height: 220px
display: flex
flex-direction: column
justify-content: center
font-family: 'MiSans VF', sans-serif
/* Make chips inside banner-title scale with viewport while staying readable */
.banner-title .mdui-chip,
.banner-title mdui-chip
font-size: clamp(0.85rem, 1.2vw, 1rem)
padding: 0.25rem 0.6rem
display: inline-flex
align-items: center
gap: 0.4rem
height: auto
min-height: 32px
line-height: 1
border-radius: 999px
/* shorten chips so they appear compact in the banner */
max-width: 160px
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
.banner-title .mdui-chip mdui-icon,
.banner-title mdui-chip mdui-icon
font-size: 1.05em
@media (max-width: 480px)
.banner-title .mdui-chip,
.banner-title mdui-chip
font-size: 0.85rem
padding: 0.18rem 0.45rem
height: auto
weight: auto
.banner-title-text1
margin: 0.5rem 0
line-height: var(--mdui-typescale-display-small-line-height)
font-size: var(--mdui-typescale-display-small-size)
letter-spacing: var(--mdui-typescale-display-small-tracking)
font-weight: var(--mdui-typescale-display-small-weight)
.banner-title-text2
margin: 0.5rem 0
line-height: var(--mdui-typescale-body-large-line-height)
font-size: var(--mdui-typescale-body-large-size)
letter-spacing: var(--mdui-typescale-body-large-tracking)
font-weight: var(--mdui-typescale-body-large-weight)
.banner-sentence
width: 100%
.banner-image
width: 30%
min-height: 200px
height: auto
background: none
position: relative
overflow: hidden
z-index: 0
padding: 12px 0
> *
position: relative
z-index: 1
.banner-image::before
content: ''
position: absolute
inset: 0
background-image: url('https://blog.yizhixiaozhu.top/images/1.png')
background-size: cover
background-position: center center
background-repeat: no-repeat
filter: blur(8px)
transform: scale(1.1)
z-index: 0
/* Ensure hitokoto chip wraps and doesn't overflow the banner width */
.banner-hitokoto
max-width: 100%
display: inline-flex
align-items: center
justify-content: center
white-space: normal
overflow-wrap: anywhere
word-break: break-word
padding: 0.25rem 0.5rem
.banner-hitokoto a
display: inline-block
max-width: 100%
white-space: normal
overflow-wrap: anywhere
#hitokoto_inner
display: inline-block
white-space: normal
max-width: calc(100% - 22px)
overflow-wrap: anywhere
word-break: break-word
#hitokoto_cursor
display: inline-block
margin-left: 6px
/* Responsive: hide banner-image on small screens and stack title above image */
@media (max-width: 768px)
.banner-container
flex-direction: column
gap: 0.5rem
.banner-image
display: none
.banner-title
width: 100%

View File

@ -0,0 +1,182 @@
.articles-container {
margin: 0 auto;
padding: 0 16px;
column-count: 3;
}
.article-card {
margin: 20px 0;
transition: transform 0.2s ease, box-shadow 0.2s ease;
cursor: pointer;
display: flex;
flex-direction: column;
break-inside: avoid;
}
.article-card:hover {
transform: translateY(-3px);
}
.article-content {
padding: 1.25rem;
width: 100%;
display: flex;
flex-direction: column;
gap: 16px;
height: 100%;
box-sizing: border-box;
}
.article-cover {
width: 100%;
height: 160px;
border-radius: 8px;
overflow: hidden;
flex-shrink: 0;
}
.article-cover img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
}
.article-main {
display: flex;
flex-direction: column;
gap: 0.75rem;
flex: 1;
min-height: 0;
}
.article-title {
line-height: var(--mdui-typescale-headline-small-line-height);
font-size: var(--mdui-typescale-headline-small-size);
letter-spacing: var(--mdui-typescale-headline-small-tracking);
font-weight: var(--mdui-typescale-headline-small-weight);
/* */
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.article-content span {
/* */
color: var(--mdui-color-on-surface-variant);
font-size: 1.0rem;
line-height: 1.5;
/* - 6120 */
display: -webkit-box;
-webkit-line-clamp: 6;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
max-height: 9em;
/* */
word-wrap: break-word;
word-break: break-word;
white-space: normal;
}
.article-meta {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
margin-top: auto;
flex-wrap: wrap;
}
.article-date {
color: var(--mdui-color-on-surface-variant);
font-size: 0.8rem;
display: flex;
align-items: center;
gap: 0.25rem;
flex-shrink: 0;
}
.article-tags {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
justify-content: flex-end;
flex: 1;
}
.article-tags mdui-chip {
flex-shrink: 0;
}
.article-side {
display: flex;
justify-content: flex-end;
margin-top: 10px;
flex-shrink: 0;
}
.article-read-more {
display: flex;
align-items: center;
gap: 0.5rem;
white-space: nowrap;
}
.no-articles {
text-align: center;
padding: 3rem 2rem;
border-radius: 16px;
min-height: 200px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
@media (max-width: 1200px) {
.articles-container {
column-count: 2;
}
.article-content span {
-webkit-line-clamp: 5;
max-height: 7.5em;
}
}
@media (max-width: 768px) {
.articles-container {
column-count: 1;
}
.article-meta {
flex-direction: column;
align-items: flex-start;
gap: 0.75rem;
}
.article-tags {
justify-content: flex-start;
width: 100%;
}
.article-content span {
-webkit-line-clamp: 4;
max-height: 6em;
}
}
@media (max-width: 500px) {
.article-content {
padding: 1rem;
gap: 12px;
}
.article-content span {
-webkit-line-clamp: 3;
max-height: 4.5em;
}
}

View File

@ -0,0 +1,370 @@
/* MDUI 风格的页面跳转动画 */
.page-transition-container {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
/* 添加模糊效果的类 */
.page-transition-blur {
filter: blur(5px);
transition: filter 0.3s ease;
}
/* ===== 滑入滑出动画 ===== */
.page-slide-enter {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateX(8%);
opacity: 0;
z-index: 1;
}
.page-slide-enter-active {
animation: mdui-slide-in 300ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
.page-slide-exit {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateX(0);
opacity: 1;
z-index: 0;
}
.page-slide-exit-active {
animation: mdui-slide-out 300ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
/* MDUI 标准滑入动画 */
@keyframes mdui-slide-in {
0% {
transform: translateX(8%);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
/* MDUI 标准滑出动画 */
@keyframes mdui-slide-out {
0% {
transform: translateX(0);
opacity: 1;
}
100% {
transform: translateX(-4%);
opacity: 0;
}
}
/* ===== 淡入淡出动画 ===== */
.page-fade-enter {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
z-index: 1;
}
.page-fade-enter-active {
animation: mdui-fade-in 280ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
.page-fade-exit {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 1;
z-index: 0;
}
.page-fade-exit-active {
animation: mdui-fade-out 280ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
/* MDUI 标准淡入动画 */
@keyframes mdui-fade-in {
0% {
opacity: 0;
transform: scale(0.98);
}
100% {
opacity: 1;
transform: scale(1);
}
}
/* MDUI 标准淡出动画 */
@keyframes mdui-fade-out {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(1.02);
}
}
/* ===== 上升下降动画 ===== */
.page-lift-enter {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateY(12px);
opacity: 0;
z-index: 1;
}
.page-lift-enter-active {
animation: mdui-lift-in 320ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
.page-lift-exit {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateY(0);
opacity: 1;
z-index: 0;
}
.page-lift-exit-active {
animation: mdui-lift-out 320ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
/* MDUI 上升进入动画 */
@keyframes mdui-lift-in {
0% {
transform: translateY(12px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
/* MDUI 下降退出动画 */
@keyframes mdui-lift-out {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-8px);
opacity: 0;
}
}
/* ===== 缩放动画 ===== */
.page-scale-enter {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: scale(0.92);
opacity: 0;
z-index: 1;
}
.page-scale-enter-active {
animation: mdui-scale-in 280ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
.page-scale-exit {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: scale(1);
opacity: 1;
z-index: 0;
}
.page-scale-exit-active {
animation: mdui-scale-out 280ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
/* MDUI 缩放进入动画 */
@keyframes mdui-scale-in {
0% {
transform: scale(0.92);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
/* MDUI 缩放退出动画 */
@keyframes mdui-scale-out {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(1.08);
opacity: 0;
}
}
/* ===== 卡片展开动画 ===== */
.page-card-enter {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: scale(0.8) translateY(20px);
opacity: 0;
border-radius: 16px;
z-index: 1;
}
.page-card-enter-active {
animation: mdui-card-in 350ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
.page-card-exit {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: scale(1) translateY(0);
opacity: 1;
border-radius: 0;
z-index: 0;
}
.page-card-exit-active {
animation: mdui-card-out 350ms cubic-bezier(0.2, 0, 0, 1) forwards;
}
/* MDUI 卡片展开动画 */
@keyframes mdui-card-in {
0% {
transform: scale(0.8) translateY(20px);
opacity: 0;
border-radius: 16px;
}
100% {
transform: scale(1) translateY(0);
opacity: 1;
border-radius: 0;
}
}
/* MDUI 卡片收缩动画 */
@keyframes mdui-card-out {
0% {
transform: scale(1) translateY(0);
opacity: 1;
border-radius: 0;
}
100% {
transform: scale(1.1) translateY(-10px);
opacity: 0;
border-radius: 12px;
}
}
/* ===== 响应式优化 ===== */
@media (max-width: 600px) {
.page-slide-enter {
transform: translateX(4%);
}
@keyframes mdui-slide-out {
0% {
transform: translateX(0);
opacity: 1;
}
100% {
transform: translateX(-2%);
opacity: 0;
}
}
.page-lift-enter {
transform: translateY(8px);
}
@keyframes mdui-lift-out {
0% {
transform: translateY(0);
opacity: 1;
}
100% {
transform: translateY(-4px);
opacity: 0;
}
}
}
/* ===== 性能优化 ===== */
.page-transition-container {
/* 启用GPU加速 */
transform: translateZ(0);
backface-visibility: hidden;
perspective: 1000px;
}
.page-slide-enter,
.page-slide-exit,
.page-fade-enter,
.page-fade-exit,
.page-lift-enter,
.page-lift-exit,
.page-scale-enter,
.page-scale-exit,
.page-card-enter,
.page-card-exit {
/* 优化动画性能 */
will-change: transform, opacity;
backface-visibility: hidden;
}
/* ===== 无障碍支持 ===== */
@media (prefers-reduced-motion: reduce) {
.page-transition-container * {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* Ajax加载状态样式 */
#main-content {
transition: opacity 0.3s ease;
}
/* 加载指示器 */
.ajax-loading {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 9999;
}

View File

@ -0,0 +1,56 @@
.sidebar-content
top: 64px
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important
.sidebar-user-card
text-align: left
padding: 2rem 1.5rem
margin: 0.125rem 0.5rem
display: block
border: 0
.sidebar-user-avatar
width: 80px
height: 80px
margin: 0
display: block
.sidebar-user-name
line-height: var(--mdui-typescale-headline-medium-line-height)
font-size: var(--mdui-typescale-headline-medium-size)
letter-spacing: var(--mdui-typescale-headline-medium-tracking)
font-weight: var(--mdui-typescale-headline-medium-weight)
.sidebar-user-slogen
line-height: var(--mdui-typescale-title-medium-line-height)
font-size: var(--mdui-typescale-title-medium-size)
letter-spacing: var(--mdui-typescale-title-medium-tracking)
font-weight: var(--mdui-typescale-title-medium-weight)
.nav-list
padding: 0.5rem
.nav-item
margin: 0.1rem 0.5rem
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1)
@media (max-width: 840px)
.sidebar-content::before
content: ""
position: fixed
top: 0
left: 0
right: 0
bottom: 0
background: transparent
backdrop-filter: blur(5px)
-webkit-backdrop-filter: blur(5px)
z-index: -1
opacity: 0
transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)
.sidebar-content[open]::before
opacity: 1
.sidebar-content
backdrop-filter: none

View File

@ -0,0 +1,11 @@
@font-face {
font-family: 'banner';
src: url('banner.eot');
src: url('banner.eot?#iefix') format('embedded-opentype'),
url('banner.woff2') format('woff2'),
url('banner.woff') format('woff'),
url('banner.ttf') format('truetype');
font-weight: normal;
font-style: normal;
font-display: swap;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,11 @@
@font-face {
font-family: 'DingTalk';
src: url('DingTalk.eot');
src: url('DingTalk.eot?#iefix') format('embedded-opentype'),
url('DingTalk.woff2') format('woff2'),
url('DingTalk.woff') format('woff'),
url('DingTalk.ttf') format('truetype');
font-weight: normal;
font-style: normal;
font-display: swap;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,7 @@
@font-face {
font-family: 'MiSans VF';
src: url('misansvf.ttf') format('truetype'); /* Safari, Android, iOS */
font-weight: 400;
font-style: normal;
font-display: swap;
}

View File

@ -0,0 +1,11 @@
@font-face {
font-family: 'lxwk';
src: url('lxwk.eot'); /* IE9 */
src: url('lxwk.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('lxwk.woff2') format('woff2'), /* Modern Browsers */
url('lxwk.woff') format('woff'), /* Modern Browsers */
url('lxwk.ttf') format('truetype'); /* Safari, Android, iOS */
font-weight: 400;
font-style: normal;
font-display: swap;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,10 @@
@font-face {
font-family: 'pingfang';
src: url('pingfang.woff2') format('woff2'), /* Modern Browsers */
url('pingfang.woff') format('woff'), /* Modern Browsers */
url('pingfang.ttf') format('truetype'), /* Safari, Android, iOS */
url('pingfang.otf') format('opentype'); /* OpenType */
font-weight: 200;
font-style: normal;
font-display: swap;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,19 @@
$(document).ready(function() {
$('img').each(function() {
if ($(this).parent().hasClass('fancybox')) return;
if ($(this).hasClass('nofancybox')) return;
var alt = this.alt;
if (alt) $(this).after('<span class="caption">' + alt + '</span>');
$(this).wrap('<a href="' + ($(this).attr('data-src') == null ? this.src : $(this).attr('data-src')) + '" title="' + alt + '" class="fancybox"></a>');
});
$(this).find('.fancybox').each(function(){
$(this).attr('rel', 'article');
});
});
$(document).ready(function() {
$("a[href$='.jpg'],a[href$='.png'],a[href$='.gif'],a[href$='.webp']").attr('rel', 'gallery').fancybox({
helpers : {
title: { type: 'inside'}
}
});
});

View File

View File

@ -0,0 +1,43 @@
// 页面跳转动画处理
document.addEventListener('DOMContentLoaded', function() {
// 为所有站内链接添加动画效果
const links = document.querySelectorAll('a[href]:not([target="_blank"]):not([href^="#"]):not([href^="mailto:"]):not(.no-transition)');
links.forEach(link => {
link.addEventListener('click', function(e) {
const href = this.getAttribute('href');
const isInternalLink = href.startsWith('/') || href.includes(window.location.hostname);
if (isInternalLink && href !== window.location.pathname) {
e.preventDefault();
// 添加模糊效果
const container = document.querySelector('.page-transition-container');
container.classList.add('page-transition-blur');
// 添加退出动画类
container.classList.add('page-transition-exit');
container.classList.add('page-transition-exit-active');
// 动画结束后跳转页面
setTimeout(() => {
window.location.href = href;
}, 400);
}
});
});
// 页面加载时添加进入动画
const container = document.querySelector('.page-transition-container');
if (container) {
container.classList.add('page-transition-enter');
container.classList.add('page-transition-enter-active');
// 动画结束后移除动画类和模糊效果
setTimeout(() => {
container.classList.remove('page-transition-enter');
container.classList.remove('page-transition-enter-active');
container.classList.remove('page-transition-blur');
}, 400);
}
});

View File

@ -0,0 +1,118 @@
const sidebar = document.querySelector(".sidebar-content");
const menuButton = document.querySelector(".appbar-menu-button");
const displayModeMenu = document.querySelector(".appbar-display-mode");
const displayModeButtonIcon = document.querySelector(".appbar-display-mode-icon");
const BREAKPOINT = 840;
// 从localStorage读取保存的设置如果没有则使用默认值
const savedSidebarState = localStorage.getItem('sidebarOpen');
const savedDisplayMode = localStorage.getItem('displayMode');
// 初始化侧边栏状态
if (savedSidebarState !== null) {
// 如果有保存的状态,则使用保存的状态
sidebar.open = savedSidebarState === 'true';
} else {
// 如果没有保存的状态,则根据屏幕宽度决定默认状态
sidebar.open = window.innerWidth >= BREAKPOINT;
}
// 初始化显示模式
let displayMode;
if (savedDisplayMode !== null) {
// 如果有保存的模式,则使用保存的模式
displayMode = savedDisplayMode;
displayModeMenu.value = displayMode;
} else {
// 如果没有保存的模式,则使用默认值
displayMode = displayModeMenu.value;
}
displayModeButtonIcon.name = `${displayMode}_mode`;
// 应用显示模式
if (displayMode === 'dark') {
document.body.classList.add('mdui-theme-dark');
document.body.classList.remove('mdui-theme-light');
} else if (displayMode === 'light') {
document.body.classList.add('mdui-theme-light');
document.body.classList.remove('mdui-theme-dark');
} else {
// auto mode - 根据系统设置决定
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.classList.add('mdui-theme-dark');
document.body.classList.remove('mdui-theme-light');
} else {
document.body.classList.add('mdui-theme-light');
document.body.classList.remove('mdui-theme-dark');
}
}
function changeSidebarState() {
sidebar.open = !sidebar.open;
// 保存侧边栏状态到localStorage
localStorage.setItem('sidebarOpen', sidebar.open);
}
function changeDisplayMode() {
displayMode = displayModeMenu.value;
displayModeButtonIcon.name = `${displayMode}_mode`;
// 移除所有主题类
document.body.classList.remove('mdui-theme-light', 'mdui-theme-dark');
// 应用新主题
if (displayMode === 'dark') {
document.body.classList.add('mdui-theme-dark');
} else if (displayMode === 'light') {
document.body.classList.add('mdui-theme-light');
} else {
// auto mode - 根据系统设置决定
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.body.classList.add('mdui-theme-dark');
} else {
document.body.classList.add('mdui-theme-light');
}
}
// 保存显示模式到localStorage
localStorage.setItem('displayMode', displayMode);
}
// 监听系统主题变化
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
// 只有在auto模式下才响应系统主题变化
if (displayMode === 'auto') {
document.body.classList.remove('mdui-theme-light', 'mdui-theme-dark');
if (e.matches) {
document.body.classList.add('mdui-theme-dark');
} else {
document.body.classList.add('mdui-theme-light');
}
}
});
// 定期检查侧边栏状态并保存到localStorage
setInterval(() => {
localStorage.setItem('sidebarOpen', sidebar.open);
}, 1000);
// 监听窗口大小变化自动调整侧边栏状态但不保存到localStorage
window.addEventListener('resize', function() {
if (window.innerWidth >= BREAKPOINT) {
// 大屏幕时,恢复用户保存的侧边栏状态
const savedState = localStorage.getItem('sidebarOpen');
if (savedState !== null) {
sidebar.open = savedState === 'true';
} else {
// 如果没有保存的状态,默认打开
sidebar.open = true;
}
} else {
// 小屏幕时,关闭侧边栏(但不修改保存的状态)
sidebar.open = false;
}
});
menuButton.addEventListener("click", changeSidebarState);
displayModeMenu.addEventListener("change", changeDisplayMode);