fix maybe
This commit is contained in:
parent
8d5f5f0e30
commit
928d71db75
22
source/_data/about.yml
Normal file
22
source/_data/about.yml
Normal 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
9
source/_data/links.yml
Normal 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 的个人网站
|
||||||
127
themes/materialis/.github/workflows/build.yml
vendored
Normal file
127
themes/materialis/.github/workflows/build.yml
vendored
Normal 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
139
themes/materialis/.gitignore
vendored
Normal 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-*
|
||||||
41
themes/materialis/CRITERION.md
Normal file
41
themes/materialis/CRITERION.md
Normal 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
661
themes/materialis/LICENSE
Normal 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/>.
|
||||||
34
themes/materialis/README.md
Normal file
34
themes/materialis/README.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# 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/),这会帮助你更好地描述问题,也会更快速地得到帮助。
|
||||||
159
themes/materialis/_config.yml.bak
Normal file
159
themes/materialis/_config.yml.bak
Normal 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:随机排列
|
||||||
15
themes/materialis/docs/comment.md
Normal file
15
themes/materialis/docs/comment.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# 评论系统
|
||||||
|
评论系统配置项依赖于主题配置文件,因此需要您填写。
|
||||||
|
本主题支持以下评论系统:
|
||||||
|
- Discus
|
||||||
|
- Twikoo
|
||||||
|
- Waline
|
||||||
|
您可以自行添加其他评论系统,在高级教程里会讲到。
|
||||||
|
# Giscus
|
||||||
|
giscus 加载时,会使用 GitHub Discussions 搜索 API 根据选定的映射方式(如 URL、pathname、<title> 等)来查找与当前页面关联的 discussion。如果找不到匹配的 discussion,giscus bot 就会在第一次有人留下评论或回应时自动创建一个 discussion。
|
||||||
|
[Giscus文档](https://giscus.app/zh-CN)
|
||||||
|
# Twikoo
|
||||||
|
[Twikoo文档](https://twikoo.js.org/)
|
||||||
|
# Waline
|
||||||
|
[Waline文档](https://waline.js.org/)
|
||||||
|
根据文档中的要求填写对应项
|
||||||
69
themes/materialis/docs/config.md
Normal file
69
themes/materialis/docs/config.md
Normal 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:导航项链接
|
||||||
|
|
||||||
26
themes/materialis/docs/install.md
Normal file
26
themes/materialis/docs/install.md
Normal 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主题。
|
||||||
|
恭喜你成功安装了本主题!
|
||||||
130
themes/materialis/docs/page.md
Normal file
130
themes/materialis/docs/page.md
Normal 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: 图片列表 (可选)
|
||||||
|
```
|
||||||
|
|
||||||
15
themes/materialis/docs/qexo.md
Normal file
15
themes/materialis/docs/qexo.md
Normal 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代表使用Qexo,2代表使用本主题提供的说说功能
|
||||||
|
填写Qexo的url,如:https://qexo.example.com
|
||||||
|
links页面同理,这里不再赘述
|
||||||
12
themes/materialis/index.js
Normal file
12
themes/materialis/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
console.log('Hexo Theme Materialis loaded');
|
||||||
|
|
||||||
|
|
||||||
|
hexo.on('ready', () => {
|
||||||
|
hexo.log.info('Materialis theme initialized');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {};
|
||||||
105
themes/materialis/languages/en.yml
Normal file
105
themes/materialis/languages/en.yml
Normal 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
|
||||||
105
themes/materialis/languages/fr.yml
Normal file
105
themes/materialis/languages/fr.yml
Normal 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
|
||||||
105
themes/materialis/languages/it.yml
Normal file
105
themes/materialis/languages/it.yml
Normal 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
|
||||||
105
themes/materialis/languages/ja.yml
Normal file
105
themes/materialis/languages/ja.yml
Normal 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: コピーに失敗しました。手動でコピーしてください
|
||||||
105
themes/materialis/languages/ko.yml
Normal file
105
themes/materialis/languages/ko.yml
Normal 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: 복사에 실패했습니다. 수동으로 복사해 주세요
|
||||||
106
themes/materialis/languages/zh-CN.yml
Normal file
106
themes/materialis/languages/zh-CN.yml
Normal 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: 复制失败,请手动复制
|
||||||
105
themes/materialis/languages/zh-TW.yml
Normal file
105
themes/materialis/languages/zh-TW.yml
Normal 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: 複製失敗,請手動複製
|
||||||
25
themes/materialis/layout/404.ejs
Normal file
25
themes/materialis/layout/404.ejs
Normal 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>
|
||||||
382
themes/materialis/layout/about.ejs
Normal file
382
themes/materialis/layout/about.ejs
Normal 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>
|
||||||
277
themes/materialis/layout/categories.ejs
Normal file
277
themes/materialis/layout/categories.ejs
Normal 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>
|
||||||
391
themes/materialis/layout/diary.ejs
Normal file
391
themes/materialis/layout/diary.ejs
Normal 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>
|
||||||
8
themes/materialis/layout/friend.ejs
Normal file
8
themes/materialis/layout/friend.ejs
Normal 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') %>
|
||||||
|
<% } %>
|
||||||
30
themes/materialis/layout/index.ejs
Normal file
30
themes/materialis/layout/index.ejs
Normal 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>
|
||||||
112
themes/materialis/layout/layout.ejs
Normal file
112
themes/materialis/layout/layout.ejs
Normal 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>
|
||||||
671
themes/materialis/layout/post.ejs
Normal file
671
themes/materialis/layout/post.ejs
Normal 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>
|
||||||
8
themes/materialis/layout/shuoshuo.ejs
Normal file
8
themes/materialis/layout/shuoshuo.ejs
Normal 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') %>
|
||||||
|
<% } %>
|
||||||
204
themes/materialis/layout/tag.ejs
Normal file
204
themes/materialis/layout/tag.ejs
Normal 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>
|
||||||
31
themes/materialis/layout/widgets/appbar.ejs
Normal file
31
themes/materialis/layout/widgets/appbar.ejs
Normal 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>
|
||||||
89
themes/materialis/layout/widgets/banner.ejs
Normal file
89
themes/materialis/layout/widgets/banner.ejs
Normal 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>
|
||||||
323
themes/materialis/layout/widgets/color.ejs
Normal file
323
themes/materialis/layout/widgets/color.ejs
Normal 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>
|
||||||
15
themes/materialis/layout/widgets/comments/giscus.ejs
Normal file
15
themes/materialis/layout/widgets/comments/giscus.ejs
Normal 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>
|
||||||
23
themes/materialis/layout/widgets/comments/twikoo.ejs
Normal file
23
themes/materialis/layout/widgets/comments/twikoo.ejs
Normal 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>
|
||||||
|
<% } %>
|
||||||
17
themes/materialis/layout/widgets/comments/waline.ejs
Normal file
17
themes/materialis/layout/widgets/comments/waline.ejs
Normal 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>
|
||||||
240
themes/materialis/layout/widgets/footer.ejs
Normal file
240
themes/materialis/layout/widgets/footer.ejs
Normal 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>
|
||||||
|
© <%= 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>
|
||||||
286
themes/materialis/layout/widgets/index/friend_links.ejs
Normal file
286
themes/materialis/layout/widgets/index/friend_links.ejs
Normal 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>
|
||||||
154
themes/materialis/layout/widgets/index/friend_qexo.ejs
Normal file
154
themes/materialis/layout/widgets/index/friend_qexo.ejs
Normal 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>
|
||||||
70
themes/materialis/layout/widgets/index/post-card.ejs
Normal file
70
themes/materialis/layout/widgets/index/post-card.ejs
Normal 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>
|
||||||
203
themes/materialis/layout/widgets/index/shuoshuo_qexo.ejs
Normal file
203
themes/materialis/layout/widgets/index/shuoshuo_qexo.ejs
Normal 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>
|
||||||
146
themes/materialis/layout/widgets/index/shuoshuo_theme.ejs
Normal file
146
themes/materialis/layout/widgets/index/shuoshuo_theme.ejs
Normal 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>
|
||||||
147
themes/materialis/layout/widgets/links.ejs
Normal file
147
themes/materialis/layout/widgets/links.ejs
Normal 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>
|
||||||
365
themes/materialis/layout/widgets/nav.ejs
Normal file
365
themes/materialis/layout/widgets/nav.ejs
Normal 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>
|
||||||
384
themes/materialis/layout/widgets/search.ejs
Normal file
384
themes/materialis/layout/widgets/search.ejs
Normal 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 => ({
|
||||||
|
'&': '&', '<': '<', '>': '>', '"': '"', "'": '''
|
||||||
|
}[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>
|
||||||
29
themes/materialis/layout/widgets/share.ejs
Normal file
29
themes/materialis/layout/widgets/share.ejs
Normal 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>
|
||||||
40
themes/materialis/layout/widgets/sidebar.ejs
Normal file
40
themes/materialis/layout/widgets/sidebar.ejs
Normal 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>
|
||||||
28
themes/materialis/package.json
Normal file
28
themes/materialis/package.json
Normal 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": [
|
||||||
|
"**/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
12
themes/materialis/scripts/404.js
Normal file
12
themes/materialis/scripts/404.js
Normal 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'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
10
themes/materialis/scripts/console.js
Normal file
10
themes/materialis/scripts/console.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
console.log(`
|
||||||
|
███╗ ███╗ █████╗ ████████╗███████╗██████╗ ██╗ █████╗ ██╗ ██╗███████╗
|
||||||
|
████╗ ████║██╔══██╗╚══██╔══╝██╔════╝██╔══██╗██║██╔══██╗██║ ██║██╔════╝
|
||||||
|
██╔████╔██║███████║ ██║ █████╗ ██████╔╝██║███████║██║ ██║███████╗
|
||||||
|
██║╚██╔╝██║██╔══██║ ██║ ██╔══╝ ██╔══██╗██║██╔══██║██║ ██║╚════██║
|
||||||
|
██║ ╚═╝ ██║██║ ██║ ██║ ███████╗██║ ██║██║██║ ██║███████╗██║███████║
|
||||||
|
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚══════╝╚═╝╚══════╝
|
||||||
|
`);
|
||||||
8
themes/materialis/scripts/helper.js
Normal file
8
themes/materialis/scripts/helper.js
Normal 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/';
|
||||||
|
})
|
||||||
11
themes/materialis/scripts/post-sort.js
Normal file
11
themes/materialis/scripts/post-sort.js
Normal 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);
|
||||||
|
});
|
||||||
48
themes/materialis/scripts/search-generator.js
Normal file
48
themes/materialis/scripts/search-generator.js
Normal 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)
|
||||||
|
};
|
||||||
|
});
|
||||||
300
themes/materialis/source/css/color.css
Normal file
300
themes/materialis/source/css/color.css
Normal 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;
|
||||||
|
}
|
||||||
2
themes/materialis/source/css/layout/index.styl
Normal file
2
themes/materialis/source/css/layout/index.styl
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.articles-container
|
||||||
|
max-width: 70rem
|
||||||
0
themes/materialis/source/css/layout/post.styl
Normal file
0
themes/materialis/source/css/layout/post.styl
Normal file
21
themes/materialis/source/css/style.styl
Normal file
21
themes/materialis/source/css/style.styl
Normal 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
|
||||||
0
themes/materialis/source/css/widgets/appbar.styl
Normal file
0
themes/materialis/source/css/widgets/appbar.styl
Normal file
51
themes/materialis/source/css/widgets/banner.css
Normal file
51
themes/materialis/source/css/widgets/banner.css
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
132
themes/materialis/source/css/widgets/index/banner.styl
Normal file
132
themes/materialis/source/css/widgets/index/banner.styl
Normal 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%
|
||||||
|
|
||||||
182
themes/materialis/source/css/widgets/index/post-card.styl
Normal file
182
themes/materialis/source/css/widgets/index/post-card.styl
Normal 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;
|
||||||
|
/* 防止摘要溢出 - 限制最多显示6行(约120字) */
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
370
themes/materialis/source/css/widgets/page-transition.css
Normal file
370
themes/materialis/source/css/widgets/page-transition.css
Normal 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;
|
||||||
|
}
|
||||||
56
themes/materialis/source/css/widgets/sidebar.styl
Normal file
56
themes/materialis/source/css/widgets/sidebar.styl
Normal 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
|
||||||
11
themes/materialis/source/fonts/banner/banner.css
Normal file
11
themes/materialis/source/fonts/banner/banner.css
Normal 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;
|
||||||
|
}
|
||||||
BIN
themes/materialis/source/fonts/banner/banner.eot
Normal file
BIN
themes/materialis/source/fonts/banner/banner.eot
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/banner/banner.ttf
Normal file
BIN
themes/materialis/source/fonts/banner/banner.ttf
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/banner/banner.woff
Normal file
BIN
themes/materialis/source/fonts/banner/banner.woff
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/banner/banner.woff2
Normal file
BIN
themes/materialis/source/fonts/banner/banner.woff2
Normal file
Binary file not shown.
11
themes/materialis/source/fonts/dingtalk/DingTalk.css
Normal file
11
themes/materialis/source/fonts/dingtalk/DingTalk.css
Normal 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;
|
||||||
|
}
|
||||||
BIN
themes/materialis/source/fonts/dingtalk/DingTalk.eot
Normal file
BIN
themes/materialis/source/fonts/dingtalk/DingTalk.eot
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/dingtalk/DingTalk.ttf
Normal file
BIN
themes/materialis/source/fonts/dingtalk/DingTalk.ttf
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/dingtalk/DingTalk.woff
Normal file
BIN
themes/materialis/source/fonts/dingtalk/DingTalk.woff
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/dingtalk/DingTalk.woff2
Normal file
BIN
themes/materialis/source/fonts/dingtalk/DingTalk.woff2
Normal file
Binary file not shown.
7
themes/materialis/source/fonts/lxwk/lxwk.css
Normal file
7
themes/materialis/source/fonts/lxwk/lxwk.css
Normal 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;
|
||||||
|
}
|
||||||
11
themes/materialis/source/fonts/lxwk/lxwk.css.bak
Normal file
11
themes/materialis/source/fonts/lxwk/lxwk.css.bak
Normal 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;
|
||||||
|
}
|
||||||
BIN
themes/materialis/source/fonts/lxwk/lxwk.eot
Normal file
BIN
themes/materialis/source/fonts/lxwk/lxwk.eot
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/lxwk/lxwk.ttf
Normal file
BIN
themes/materialis/source/fonts/lxwk/lxwk.ttf
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/lxwk/lxwk.woff
Normal file
BIN
themes/materialis/source/fonts/lxwk/lxwk.woff
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/lxwk/lxwk.woff2
Normal file
BIN
themes/materialis/source/fonts/lxwk/lxwk.woff2
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/lxwk/misansvf.ttf
Normal file
BIN
themes/materialis/source/fonts/lxwk/misansvf.ttf
Normal file
Binary file not shown.
10
themes/materialis/source/fonts/pingfang/pingfang.css
Normal file
10
themes/materialis/source/fonts/pingfang/pingfang.css
Normal 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;
|
||||||
|
}
|
||||||
BIN
themes/materialis/source/fonts/pingfang/pingfang.otf
Normal file
BIN
themes/materialis/source/fonts/pingfang/pingfang.otf
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/pingfang/pingfang.ttf
Normal file
BIN
themes/materialis/source/fonts/pingfang/pingfang.ttf
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/pingfang/pingfang.woff
Normal file
BIN
themes/materialis/source/fonts/pingfang/pingfang.woff
Normal file
Binary file not shown.
BIN
themes/materialis/source/fonts/pingfang/pingfang.woff2
Normal file
BIN
themes/materialis/source/fonts/pingfang/pingfang.woff2
Normal file
Binary file not shown.
19
themes/materialis/source/js/fancybox.js
Normal file
19
themes/materialis/source/js/fancybox.js
Normal 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'}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
0
themes/materialis/source/js/main.js
Normal file
0
themes/materialis/source/js/main.js
Normal file
43
themes/materialis/source/js/page-transition.js
Normal file
43
themes/materialis/source/js/page-transition.js
Normal 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);
|
||||||
|
}
|
||||||
|
});
|
||||||
118
themes/materialis/source/js/widgets/appbar.js
Normal file
118
themes/materialis/source/js/widgets/appbar.js
Normal 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);
|
||||||
Loading…
x
Reference in New Issue
Block a user