├── .commitlintrc.js
├── .editorconfig
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug-report.yml
│ ├── config.yml
│ ├── feature-request.yml
│ └── other.yml
└── workflows
│ ├── auto-merge.yml
│ └── release.yml
├── .gitignore
├── .husky
├── commit-msg
└── pre-commit
├── .npmignore
├── .prettierrc
├── .stylelintrc.js
├── CHANGELOG.md
├── LICENSE
├── README.md
├── _config.yml
├── docs
├── README_zh-CN.md
└── README_zh-TW.md
├── languages
├── en.yml
├── zh-CN.yml
└── zh-TW.yml
├── layout
├── _page
│ ├── 404.ejs
│ ├── archive.ejs
│ ├── category-archive.ejs
│ ├── category.ejs
│ ├── home.ejs
│ ├── post.ejs
│ ├── tag-archive.ejs
│ └── tag.ejs
├── _partial
│ ├── archive-list.ejs
│ ├── comment
│ │ ├── comment.ejs
│ │ ├── config-error.ejs
│ │ ├── load-handler.ejs
│ │ └── plugins
│ │ │ ├── artalk.ejs
│ │ │ ├── disqus.ejs
│ │ │ ├── giscus.ejs
│ │ │ ├── gitalk.ejs
│ │ │ ├── twikoo.ejs
│ │ │ ├── valine.ejs
│ │ │ └── waline.ejs
│ ├── common
│ │ ├── empty-content.ejs
│ │ └── mermaid.ejs
│ ├── first-screen.ejs
│ ├── footer.ejs
│ ├── head.ejs
│ ├── header.ejs
│ ├── image-mask.ejs
│ ├── local-search.ejs
│ ├── paginator.ejs
│ ├── pjax
│ │ └── pjax.ejs
│ ├── post
│ │ ├── post-copyright-info.ejs
│ │ ├── post-meta-info.ejs
│ │ ├── post-share.ejs
│ │ ├── post-tools.ejs
│ │ └── reward-author.ejs
│ ├── progress-bar.ejs
│ ├── scripts.ejs
│ ├── side-tools.ejs
│ └── toc.ejs
├── _template
│ ├── friends-link.ejs
│ ├── page-template.ejs
│ ├── photo-album.ejs
│ └── tools-nav.ejs
├── index.ejs
├── layout.ejs
└── page.ejs
├── package.json
├── scripts
├── events
│ ├── 404-page.js
│ ├── config-handle.js
│ └── keep-info.js
├── filters
│ ├── encrypt-handle.js
│ ├── image-handle.js
│ └── link-handle.js
├── helpers
│ ├── export-config.js
│ └── helper.js
└── tags
│ ├── button.js
│ ├── index.js
│ ├── note.js
│ └── tabs.js
└── source
├── css
├── common
│ ├── animated.styl
│ ├── basic.styl
│ ├── code-block
│ │ ├── code-block.styl
│ │ ├── code-theme.styl
│ │ └── highlight.styl
│ ├── css-variables.styl
│ ├── keep-style.styl
│ ├── markdown.styl
│ ├── stylus-variables.styl
│ └── tags
│ │ ├── keep-button.styl
│ │ ├── keep-note.styl
│ │ └── keep-tabs.styl
├── layout
│ ├── _page
│ │ ├── 404.styl
│ │ ├── archive.styl
│ │ ├── category-archive.styl
│ │ ├── category.styl
│ │ ├── home.styl
│ │ ├── post.styl
│ │ ├── tag-archive.styl
│ │ └── tag.styl
│ ├── _partial
│ │ ├── archive-list.styl
│ │ ├── comment
│ │ │ ├── comment.styl
│ │ │ ├── gitalk.styl
│ │ │ ├── twikoo.styl
│ │ │ ├── valine.styl
│ │ │ └── waline.styl
│ │ ├── common
│ │ │ └── empty-content.styl
│ │ ├── first-screen.styl
│ │ ├── footer.styl
│ │ ├── header.styl
│ │ ├── image-mask.styl
│ │ ├── local-search.styl
│ │ ├── paginator.styl
│ │ ├── post-meta-info.styl
│ │ ├── post
│ │ │ ├── post-copyright-info.styl
│ │ │ ├── post-share.styl
│ │ │ ├── post-tools.styl
│ │ │ └── reward-author.styl
│ │ ├── progress-bar.styl
│ │ ├── side-tools.styl
│ │ └── toc.styl
│ ├── _template
│ │ ├── friends-link.styl
│ │ ├── page-template.styl
│ │ ├── photo-album.styl
│ │ └── tools-nav.styl
│ └── page.styl
└── style.styl
├── font
├── css
│ ├── brands.min.css
│ ├── fontawesome.min.css
│ ├── regular.min.css
│ └── solid.min.css
└── webfonts
│ ├── fa-brands-400.ttf
│ ├── fa-brands-400.woff2
│ ├── fa-regular-400.ttf
│ ├── fa-regular-400.woff2
│ ├── fa-solid-900.ttf
│ ├── fa-solid-900.woff2
│ ├── fa-v4compatibility.ttf
│ └── fa-v4compatibility.woff2
├── images
├── avatar.svg
├── bg.svg
├── bg2.svg
├── brands
│ ├── aliyun.png
│ ├── cloudflare.png
│ ├── gitee.png
│ ├── github.png
│ ├── netlify.png
│ ├── tencent_cloud.png
│ ├── upyun.png
│ └── vercel.png
└── logo.svg
└── js
├── back2top.js
├── code-block.js
├── header-shrink.js
├── lazyload.js
├── libs
├── anime.min.js
└── pjax.min.js
├── local-search.js
├── main.js
├── page
├── category-page.js
├── home-page.js
├── links-page.js
├── photos-page.js
└── tools-page.js
├── post
├── copyright-info.js
├── post-helper.js
├── share.js
└── toc.js
├── toggle-theme.js
└── utils.js
/.commitlintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional'],
3 | rules: {
4 | 'type-enum': [
5 | 2,
6 | 'always',
7 | [
8 | 'feat',
9 | 'fix',
10 | 'docs',
11 | 'style',
12 | 'refactor',
13 | 'perf',
14 | 'test',
15 | 'build',
16 | 'revert',
17 | 'ci',
18 | 'ui',
19 | 'chore'
20 | ]
21 | ]
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ['https://xpoet.cn/post/0054d541d734/']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report | Bug 报告
2 | description: Something isn't working as expected
3 | #title: ""
4 | labels:
5 | - bug
6 | #assignees: ""
7 | body:
8 | - type: markdown
9 | id: overall
10 | attributes:
11 | value: |
12 | Please follow this Issue template to provide relevant information, such as source code repository, website URL and screenshots, which will help us investigate.
13 | 请按照此 Issue 模版提供相关信息,例如源码仓库、网站链接和屏幕截图,这将有助于我们进行排查。
14 |
15 | - type: checkboxes
16 | id: checklist
17 | attributes:
18 | label: Issue Checklist | Issue 检查清单
19 | description: |
20 | I made sure I checked
21 | 我确认我已经检查过了
22 | options:
23 | - label: I am using Keep version `4.2.5` or later. (使用 Keep `4.2.5` 或更高版本)
24 | required: true
25 | - label: I have already read the [Troubleshooting page of Hexo](https://hexo.io/docs/troubleshooting) and [Keep documents](https://keep-docs.xpoet.cn). (已阅读 [Hexo 故障处理页面](https://hexo.io/docs/troubleshooting) 和 [Keep 文档](https://keep-docs.xpoet.cn))
26 | required: true
27 | - label: I have already searched for current [issues](https://github.com/XPoet/hexo-theme-keep/issues), which does not help me. (已搜索 [Issues](https://github.com/XPoet/hexo-theme-keep/issues),对我没有帮助)
28 | required: true
29 |
30 | - type: textarea
31 | id: expected-behavior
32 | attributes:
33 | label: Expected behavior | 预期行为
34 | description: |
35 | Please provide a detailed description of what you consider expected behavior.
36 | 请仔细描述你认为的预期行为
37 | validations:
38 | required: true
39 |
40 | - type: textarea
41 | id: actual-behavior
42 | attributes:
43 | label: Actual behavior | 实际行为
44 | description: |
45 | Please provide the following information
46 | 请提供以下信息
47 | value: |
48 | - Links to demo site with this issue (链接到这个 Issue 的演示网站)
49 | - Links to source code of the blog with this issue (链接到这个 Issue 的博客源代码)
50 | - Screenshots(屏幕截图)
51 |
52 | validations:
53 | required: true
54 |
55 | - type: textarea
56 | id: reproduce
57 | attributes:
58 | label: Steps to reproduce the behavior | 重现步骤
59 | description: |
60 | Please provide a detailed description of the steps to reproduce the issue
61 | 请仔细描述重现步骤
62 | value: |
63 | 1. First step ... (第一步 ...)
64 | 2. Second step ... (第二步 ...)
65 | 3. Third step ... (第三步 ...)
66 |
67 | validations:
68 | required: true
69 |
70 | - type: markdown
71 | id: env-info
72 | attributes:
73 | value: |
74 | ## Environment Information | 环境信息
75 |
76 | - type: textarea
77 | id: nodejs-info
78 | attributes:
79 | label: Node.js and NPM Information | Node.js 和 NPM 信息
80 | description: |
81 | Paste output from `node -v && npm -v`
82 | 粘贴 `node -v && npm -v` 输出的信息
83 | render: Text
84 | validations:
85 | required: true
86 |
87 | - type: textarea
88 | id: hexo-config
89 | attributes:
90 | label: Hexo Configuration | Hexo 配置
91 | description: |
92 | "Paste configuration from Hexo `_config.yml`
93 | 粘贴 Hexo `_config.yml` 中的内容
94 | render: YAML
95 | validations:
96 | required: true
97 |
98 | - type: textarea
99 | id: keep-config
100 | attributes:
101 | label: Keep Configuration | Keep 配置
102 | description: |
103 | Paste ONLY CHANGED CONFIGURATION from Keep `_config.yml`
104 | 只粘贴 Keep 主题配置文件 `_config.yml` 中修改过的部分
105 | render: YAML
106 | validations:
107 | required: true
108 |
109 | - type: textarea
110 | id: other-info
111 | attributes:
112 | label: Other Information | 其它信息
113 | description: |
114 | e.g. Browser, System
115 | 例如:浏览器,操作系统
116 | validations:
117 | required: false
118 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Keep Documents | Keep 文档
4 | url: https://keep-docs.xpoet.cn
5 | about: Official user manual of Keep Theme
6 |
7 | - name: GitHub Discussions
8 | url: https://github.com/XPoet/hexo-theme-keep/discussions
9 | about: Please ask and answer questions here.
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request.yml:
--------------------------------------------------------------------------------
1 | name: Feature Request | 功能需求
2 | description: Suggest an idea for this project
3 | #title: ""
4 | labels:
5 | - feature request
6 | #assignees: ""
7 | body:
8 | - type: markdown
9 | id: overall
10 | attributes:
11 | value: |
12 | Please follow this Issue template to provide relevant information, such as source code repository, website URL and screenshots, which will help us investigate.
13 | 请按照此 Issue 模版提供相关信息,例如源码仓库、网站链接和屏幕截图,这将有助于我们进行排查。
14 |
15 | - type: checkboxes
16 | id: checklist
17 | attributes:
18 | label: Issue Checklist | Issue 检查清单
19 | description: |
20 | I made sure I checked
21 | 我确认我已经检查过了
22 | options:
23 | - label: I am using Keep version `4.2.5` or later. (使用 Keep `4.2.5` 或更高版本)
24 | required: true
25 | - label: I have already read the [Troubleshooting page of Hexo](https://hexo.io/docs/troubleshooting) and [Keep documents](https://keep-docs.xpoet.cn). (已阅读 [Hexo 故障处理页面](https://hexo.io/docs/troubleshooting) 和 [Keep 文档](https://keep-docs.xpoet.cn))
26 | required: true
27 | - label: I have already searched for current [issues](https://github.com/XPoet/hexo-theme-keep/issues), which does not help me. (已搜索 [Issues](https://github.com/XPoet/hexo-theme-keep/issues),对我没有帮助)
28 | required: true
29 |
30 | - type: textarea
31 | id: feature-request
32 | attributes:
33 | label: Feature Request | 功能需求
34 | description: |
35 | Please provide the following information
36 | 请提供以下信息
37 | value: |
38 | - Detailed description of feature request (功能需求的详情描述)
39 | - This feature request reference link (功能需求的参考链接)
40 | - This feature request reference screenshots (功能需求的参考截图)
41 | validations:
42 | required: true
43 |
44 | - type: textarea
45 | id: other-info
46 | attributes:
47 | label: Other Information | 其它信息
48 | description: |
49 | Additional information
50 | 补充其它信息
51 | validations:
52 | required: false
53 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/other.yml:
--------------------------------------------------------------------------------
1 | name: Other | 其他
2 | description: Not a feature request or bug report
3 | #title: ""
4 | labels:
5 | - question
6 | #assignees: ""
7 | body:
8 | - type: markdown
9 | id: overall
10 | attributes:
11 | value: |
12 | Please follow this Issue template to provide relevant information, such as source code repository, website URL and screenshots, which will help us investigate.
13 | 请按照此 Issue 模版提供相关信息,例如源码仓库、网站链接和屏幕截图,这将有助于我们进行排查。
14 |
15 | - type: checkboxes
16 | id: checklist
17 | attributes:
18 | label: Issue Checklist | Issue 检查清单
19 | description: |
20 | I made sure I checked
21 | 我确认我已经检查过了
22 | options:
23 | - label: I am using Keep version `4.2.5` or later. (使用 Keep `4.2.5` 或更高版本)
24 | required: true
25 | - label: I have already read the [Troubleshooting page of Hexo](https://hexo.io/docs/troubleshooting) and [Keep documents](https://keep-docs.xpoet.cn). (已阅读 [Hexo 故障处理页面](https://hexo.io/docs/troubleshooting) 和 [Keep 文档](https://keep-docs.xpoet.cn))
26 | required: true
27 | - label: I have already searched for current [issues](https://github.com/XPoet/hexo-theme-keep/issues), which does not help me. (已搜索 [Issues](https://github.com/XPoet/hexo-theme-keep/issues),对我没有帮助)
28 | required: true
29 |
30 | - type: textarea
31 | id: other-info
32 | attributes:
33 | label: Other Information | 其它信息
34 | description: |
35 | Feedback other questions
36 | 反馈其它的问题
37 | validations:
38 | required: true
39 |
--------------------------------------------------------------------------------
/.github/workflows/auto-merge.yml:
--------------------------------------------------------------------------------
1 | name: automerge
2 | on:
3 | pull_request:
4 | types:
5 | - labeled
6 | - unlabeled
7 | - synchronize
8 | - opened
9 | - edited
10 | - ready_for_review
11 | - reopened
12 | - unlocked
13 | pull_request_review:
14 | types:
15 | - submitted
16 | check_suite:
17 | types:
18 | - completed
19 | status: {}
20 | jobs:
21 | automerge:
22 | runs-on: ubuntu-latest
23 | steps:
24 | - id: automerge
25 | name: automerge
26 | uses: pascalgn/automerge-action@v0.15.6
27 | env:
28 | GITHUB_TOKEN: ${{ secrets.ACTIONS_TOKEN }}
29 | MERGE_LABELS: "autorelease: pending"
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - master
5 |
6 | name: release
7 |
8 | jobs:
9 | release:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: google-github-actions/release-please-action@v3
13 | id: release
14 | with:
15 | token: ${{ secrets.ACTIONS_TOKEN }}
16 | release-type: node
17 | package-name: standard-version
18 | changelog-types: '[
19 | {
20 | "type": "feat",
21 | "section": "🌟 Features",
22 | "hidden": false
23 | },
24 | {
25 | "type": "fix",
26 | "section": "🐞 Bug Fixes",
27 | "hidden": false
28 | },
29 | {
30 | "type": "docs",
31 | "section": "📖 Docs",
32 | "hidden": false
33 | },
34 | {
35 | "type": "style",
36 | "section": "🎨 Styling",
37 | "hidden": false
38 | },
39 | {
40 | "type": "refactor",
41 | "section": "🍭 Code Refactoring",
42 | "hidden": false
43 | },
44 | {
45 | "type": "perf",
46 | "section": "🛠 Performance Improvements",
47 | "hidden": false
48 | },
49 | {
50 | "type": "test",
51 | "section": "🚧 Tests",
52 | "hidden": false
53 | },
54 | {
55 | "type": "build",
56 | "section": "📦 Build System",
57 | "hidden": false
58 | },
59 | {
60 | "type": "revert",
61 | "section": "🌀 Reverts",
62 | "hidden": false
63 | },
64 | {
65 | "type": "ci",
66 | "section": "🎯 CI",
67 | "hidden": false
68 | },
69 | {
70 | "type": "ui",
71 | "section": "💄 Improvement UI",
72 | "hidden": false
73 | },
74 | {
75 | "type": "chore",
76 | "section": "🚦 Chore",
77 | "hidden": false
78 | }
79 | ]'
80 |
81 | # The logic below handles the npm publication
82 |
83 | - uses: actions/checkout@v2
84 | if: ${{ steps.release.outputs.release_created }}
85 |
86 | - uses: actions/setup-node@v1
87 | with:
88 | node-version: 16
89 | registry-url: https://registry.npmjs.org
90 | if: ${{ steps.release.outputs.release_created }}
91 |
92 | - run: npm install
93 | if: ${{ steps.release.outputs.release_created }}
94 |
95 | - run: npm publish
96 | env:
97 | NODE_AUTH_TOKEN: ${{secrets.NPM_PUBLISH_TOKEN}}
98 | if: ${{ steps.release.outputs.release_created }}
99 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /tmp
6 | /out-tsc
7 |
8 | # dependencies
9 | /node_modules
10 | package-lock.json
11 | yarn.lock
12 |
13 | # IDEs and editors
14 | /.idea
15 | .project
16 | .classpath
17 | .c9/
18 | *.launch
19 | .settings/
20 | *.sublime-workspace
21 |
22 | # IDE - VSCode
23 | .vscode/*
24 | # !.vscode/settings.json
25 | # !.vscode/tasks.json
26 | # !.vscode/launch.json
27 | # !.vscode/extensions.json
28 |
29 | # misc
30 | /.sass-cache
31 | /connect.lock
32 | /coverage
33 | /libpeerconnection.log
34 | npm-debug.log
35 | testem.log
36 | /typings
37 |
38 | # e2e
39 | /e2e/src/*.js
40 | /e2e/src/*.map
41 | /cypress/screenshots
42 |
43 | # System Files
44 | .DS_Store
45 | Thumbs.db
46 |
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx commitlint --edit
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | Thumbs.db
3 | node_modules
4 | package-lock.json
5 | yarn.lock
6 | pnpm-lock.yaml
7 | .github/*
8 | .vscode/*
9 | .idea/*
10 | .husky/*
11 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": false,
3 | "endOfLine": "auto",
4 | "tabWidth": 2,
5 | "printWidth": 100,
6 | "singleQuote": true,
7 | "trailingComma": "none",
8 | "bracketSpacing": true,
9 | "semi": false
10 | }
11 |
--------------------------------------------------------------------------------
/.stylelintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'stylelint-config-rational-order',
4 | 'stylelint-stylus/standard'
5 | ],
6 | rules: {
7 | 'stylus/pythonic': 'never'
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Language:
3 | English |
4 |
简体中文 |
5 |
繁體中文
6 |
7 |
8 |
9 |
10 | # hexo-theme-keep
11 |
12 | **Keep** is a simple and light [Hexo](https://hexo.io) theme. It makes you more focused on writing.
13 |
14 | [](https://github.com/XPoet/hexo-theme-keep/blob/master/LICENSE)
15 | [](https://github.com/XPoet/hexo-theme-ils/releases)
16 | [](https://www.npmjs.com/package/hexo-theme-keep)
17 | [](https://www.npmjs.com/package/hexo-theme-keep)
18 | [](https://nodejs.org)
19 | [](https://hexo.io)
20 |
21 | ## :star2: Online Preview
22 |
23 | - **[XPoet's Blog](https://xpoet.cn/)**
24 | - **[Keep Demo](https://keep.xpoet.cn/)**
25 |
26 | > If you want to put a link to your blog in the online preview list, you can give the author a PR.
27 |
28 | ## :books: Documents
29 |
30 | Please see the official documents of **Keep**.
31 |
32 | - **[Keep official document](https://keep-docs.xpoet.cn/)**
33 | - **[Keep theme configuration guide](https://keep-docs.xpoet.cn/basis/configuration-guide/base_info.html)**
34 | - **[Keep theme advanced usage](https://keep-docs.xpoet.cn/advanced/set-language.html)**
35 |
36 | ## :rocket: Installation
37 |
38 | If you're using Hexo 5.0 or later, the simplest way to install is through NPM:
39 |
40 | ```sh
41 | # recommend
42 |
43 | cd your-hexo
44 | npm install hexo-theme-keep
45 | ```
46 |
47 | Or you can clone the entire repository:
48 |
49 | ```sh
50 | cd your-hexo
51 | git clone https://github.com/XPoet/hexo-theme-keep --depth=1 themes/keep
52 | ```
53 |
54 | After the installation, open Hexo config file and set `theme` variable to `keep`.
55 |
56 | ```yml
57 | theme: keep
58 | ```
59 |
60 | ## :tada: Update
61 |
62 | **Keep** releases new versions from time to time. You can update **Keep** by the following command.
63 |
64 | Install the latest version through NPM:
65 |
66 | ```sh
67 | # recommend
68 |
69 | cd your-hexo
70 | npm install hexo-theme-keep@latest
71 | ```
72 |
73 | Or update to the latest master branch:
74 |
75 | ```sh
76 | cd themes/keep
77 | git pull
78 | ```
79 |
80 | It is not recommended to directly modify any files in the **Keep** theme. Because this may cause errors (e.g. merge conflicts), and the modified files may be discarded when upgrading the theme.
81 |
82 | ## :art: Contributors
83 |
84 | This project exists thanks to all the people who contribute.
85 |
86 |
87 |
88 |
89 |
90 | We welcome you to join the development of **Keep**. Please see [contributing guide](https://keep-docs.xpoet.cn/user-notice/contribution-guide.html).
91 |
92 | ## :sparkling_heart: Thanks
93 |
94 | **Keep** send special thanks to these great services that sponsor our core infrastructure:
95 |
96 | - [GitHub](https://github.com) allows us to host the Git repository and run the test suite.
97 | - Thanks [cdnjs](https://cdnjs.com), [jsDelivr](https://www.jsdelivr.com) and [UNPKG](https://www.unpkg.com) for providing public CDN service.
98 |
99 | ## :memo: License
100 |
101 | [AGPL-3.0](https://github.com/XPoet/hexo-theme-keep/blob/master/LICENSE)
102 |
103 | Copyright © 2020-present XPoet
104 |
105 |
--------------------------------------------------------------------------------
/docs/README_zh-CN.md:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 | # hexo-theme-keep
10 |
11 | **Keep** 是一款简约轻快的 [Hexo](https://hexo.io) 主题,它能让你更专注于写作。
12 |
13 | [](https://github.com/XPoet/hexo-theme-keep/blob/master/LICENSE)
14 | [](https://github.com/XPoet/hexo-theme-ils/releases)
15 | [](https://www.npmjs.com/package/hexo-theme-keep)
16 | [](https://www.npmjs.com/package/hexo-theme-keep)
17 | [](https://nodejs.org)
18 | [](https://hexo.io)
19 |
20 | ## :star2: 在线预览
21 |
22 | - **[XPoet's Blog](https://xpoet.cn/)**
23 | - **[keep 演示](https://keep.xpoet.cn/)**
24 |
25 | > 如果你想把自己的博客链接放置在线预览列表里,可以给作者提 PR。
26 |
27 | ## :books: 文档
28 |
29 | 请查看 **Keep** 官方文档:
30 |
31 | - **[Keep 官方文档](https://keep-docs.xpoet.cn/)**
32 | - **[Keep 主题配置指南](https://keep-docs.xpoet.cn/basis/configuration-guide/base_info.html)**
33 | - **[Keep 主题进阶使用](https://keep-docs.xpoet.cn/advanced/set-language.html)**
34 |
35 | ## :rocket: 安装
36 |
37 | 如果你使用 Hexo 5.0 或更高版本,最简单的安装方式是通过 NPM:
38 |
39 | ```sh
40 | # 推荐
41 |
42 | cd your-hexo
43 | npm install hexo-theme-keep
44 | ```
45 |
46 | 你也可以直接克隆整个仓库:
47 |
48 | ```sh
49 | cd your-hexo
50 | git clone https://github.com/XPoet/hexo-theme-keep --depth=1 themes/keep
51 | ```
52 |
53 | 安装完成后,在 Hexo 配置文件中将 `theme` 设置为 `keep`。
54 |
55 | ```yml
56 | theme: keep
57 | ```
58 |
59 | ## :tada: 更新
60 |
61 | **Keep** 不定期发布新版本,你可以通过如下命令更新 **Keep**。
62 |
63 | 通过 NPM 安装最新版本:
64 |
65 | ```sh
66 | # 推荐
67 |
68 | $ cd your-hexo
69 | $ npm install hexo-theme-keep@latest
70 | ```
71 |
72 | 或者通过 git 更新到最新的 master 分支:
73 |
74 | ```sh
75 | $ cd themes/keep
76 | $ git pull
77 | ```
78 |
79 | 不推荐直接修改 **Keep** 主题的源文件。因为这可能导致错误(例如 git merge 冲突),并且在升级主题时修改的文件可能丢失。
80 |
81 | ## :art: 贡献者
82 |
83 | 这个项目的存在多亏了所有的贡献者。
84 |
85 |
86 |
87 |
88 |
89 | 我们欢迎你加入 **Keep** 的开发,贡献出你的一份力量。请看 [Keep 代码贡献指南](https://keep-docs.xpoet.cn/user-notice/contribution-guide.html)。
90 |
91 | ## :sparkling_heart: 鸣谢
92 |
93 | **Keep** 特别感谢这些支持我们核心基础设施的优质服务:
94 |
95 | - [GitHub](https://github.com) 容许我们托管 Git 仓库及运行测试。
96 | - [cdnjs](https://cdnjs.com)、[jsDelivr](https://www.jsdelivr.com) 和 [UNPKG](https://www.unpkg.com) 提供了 CDN 服务。
97 |
98 | ## :memo: 许可
99 |
100 | [AGPL-3.0](https://github.com/XPoet/hexo-theme-keep/blob/master/LICENSE)
101 |
102 | Copyright © 2020-present XPoet
103 |
--------------------------------------------------------------------------------
/docs/README_zh-TW.md:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 | # hexo-theme-keep
11 |
12 | **Keep** 是一款簡約輕快的 [Hexo](https://hexo.io) 主題,它能讓你更專注於寫作。
13 |
14 | [](https://github.com/XPoet/hexo-theme-keep/blob/master/LICENSE)
15 | [](https://github.com/XPoet/hexo-theme-ils/releases)
16 | [](https://www.npmjs.com/package/hexo-theme-keep)
17 | [](https://www.npmjs.com/package/hexo-theme-keep)
18 | [](https://nodejs.org)
19 | [](https://hexo.io)
20 |
21 | ## :star2: 線上預覽
22 |
23 | - **[XPoet's Blog](https://xpoet.cn/)**
24 | - **[Keep 預覽](https://keep.xpoet.cn/)**
25 |
26 | > 如果你想把自己的部落格連結放在線上預覽列表裏,可以給作者提 PR。
27 |
28 | ## :books: 文檔
29 |
30 | 請查看 **Keep** 官方文檔:
31 |
32 | - **[Keep 官方文檔](https://keep-docs.xpoet.cn/)**
33 | - **[Keep 主題配置指南](https://keep-docs.xpoet.cn/basis/configuration-guide/base_info.html)**
34 | - **[Keep 主題進階用法](https://keep-docs.xpoet.cn/advanced/set-language.html)**
35 |
36 | ## :rocket: 安装
37 |
38 | 如果你使用 Hexo 5.0 或更高版本,最簡單的安裝方式是透過 NPM:
39 |
40 | ```sh
41 | # 推薦
42 |
43 | cd your-hexo
44 | npm install hexo-theme-keep
45 | ```
46 |
47 | 你也可以直接 clone 整個原始碼庫:
48 |
49 | ```sh
50 | cd your-hexo
51 | git clone https://github.com/XPoet/hexo-theme-keep --depth=1 themes/keep
52 | ```
53 |
54 | 安裝完成後,在 Hexo 配置文件中將 `theme` 設定為 `keep`。
55 |
56 | ```yml
57 | theme: keep
58 | ```
59 |
60 | ## :tada: 更新
61 |
62 | **Keep** 不定期發布新版本。你可以透過如下命令更新 **Keep**。
63 |
64 | 透過 NPM 安裝最新版本:
65 |
66 | ```sh
67 | # 推薦
68 |
69 | $ cd your-hexo
70 | $ npm install hexo-theme-keep@latest
71 | ```
72 |
73 | 或者透過 git 更新到最新的 master 分支:
74 |
75 | ```sh
76 | $ cd themes/keep
77 | $ git pull
78 | ```
79 |
80 | 不推薦直接修改 **Keep** 主題的文件。因為這可能導致錯誤(例如 git merge 衝突),並且在更新主題時修改的文件可能丟失。
81 |
82 | ## :art: 貢獻者
83 |
84 | 這個項目的存在多虧了所有的貢獻者。
85 |
86 |
87 |
88 |
89 |
90 | 我們歡迎你加入 **Keep** 的開發,貢獻出你的一份力量。請查看 [Keep 原始碼貢獻指南](https://keep-docs.xpoet.cn/user-notice/contribution-guide.html)。
91 |
92 | ## :sparkling_heart: 銘謝
93 |
94 | **Keep** 特别感謝這些支持我們核心基本設施的優質服務:
95 |
96 | - [GitHub](https://github.com) 允許我們存放 Git 原始碼庫及運行測試。
97 | - [cdnjs](https://cdnjs.com)、[jsDelivr](https://www.jsdelivr.com) 和 [UNPKG](https://www.unpkg.com) 提供了 CDN 服務。
98 |
99 | ## :memo: 許可
100 |
101 | [AGPL-3.0](https://github.com/XPoet/hexo-theme-keep/blob/master/LICENSE)
102 |
103 | Copyright © 2020-present XPoet
104 |
--------------------------------------------------------------------------------
/languages/en.yml:
--------------------------------------------------------------------------------
1 | # ====================================================================
2 | # English
3 | # ====================================================================
4 |
5 | category: Category
6 | categories: Categories
7 | tag: Tag
8 | tags: Tags
9 | tagcloud: Tag Cloud
10 | home: Home
11 | archive: Archive
12 | archives: Archives
13 | about: About
14 | links: Links
15 | link: Link
16 | photos: Photos
17 | photo: Photo
18 | tool: Tool
19 | tools: Tools
20 | me: Me
21 | search: Search...
22 | prev: Prev
23 | next: Next
24 | prev_posts: Prev posts
25 | next_posts: Next posts
26 | page: Page %d
27 | recent_posts: Recent Posts
28 | share: Share
29 | theme: Theme
30 | rss_feed: RSS Feed
31 | top: TOP
32 | read_more: Read more
33 | wordcount: Words
34 | min2read: Mins
35 | changelog: Changelog
36 | social_contact: Social contact
37 | github: GitHub
38 | google: Google
39 | apple: Apple
40 | microsoft: Microsoft
41 | weixin: WeChat
42 | qq: QQ
43 | alipay: Alipay
44 | weibo: Weibo
45 | zhihu: Zhihu
46 | twitter: Twitter
47 | x: X
48 | facebook: Facebook
49 | email: Email
50 | code_block:
51 | copy: Copy code
52 | copied: Copied
53 | fold: Fold code block
54 | folded: Folded
55 | copy_copyright:
56 | copy: Copy copyright info
57 | copied: Copied
58 | title: Original post title
59 | author: Original post author
60 | link: Original post link
61 | copyright:
62 | author: Author
63 | title: Title
64 | link: Link
65 | create_time: Create time
66 | published: Published
67 | license: License
68 | license_title: Copyright notice
69 | license_content: "All posts in this blog are licensed under %s unless stating additionally."
70 | ago:
71 | second: "%s seconds ago"
72 | minute: "%s minutes ago"
73 | hour: "%s hours ago"
74 | day: "%s days ago"
75 | week: "%s weeks ago"
76 | month: "%s months ago"
77 | year: "%s years ago"
78 | footer:
79 | powered_by: "Powered by %s"
80 | site_uv: Unique Visitor
81 | site_pv: Page View
82 | deploy: "This site is deployed on %s"
83 | deploy_provider:
84 | github: GitHub Pages
85 | vercel: Vercel
86 | netlify: Netlify
87 | gitee: Gitee Pages
88 | cloudflare: Cloudflare
89 | aliyun: Alibaba Cloud
90 | tencent_cloud: Tencent Cloud
91 | upyun: UPYUN
92 | total_words: Total words
93 | post:
94 | aging: "It has been %s days since the last update of this post. Some contents may be outdated. Please pay attention to screening."
95 | share:
96 | qq: "Share to QQ"
97 | wechat: "Share to WeChat"
98 | weibo: "Share to WeiBo"
99 | wechat_scan: "Scan by WeChat"
100 | reward: Reward Author
101 | comment:
102 | comments: Comments
103 | fail_tip: Comment plugin failed to load
104 | reload: Click to reload
105 | loading: Loading comment plugin
106 | config_error: "Please fill in the required configuration items for %s comment plugin"
107 | 404:
108 | page_not_found: Page Not Found
109 | go_home: Take me home
110 | encryption:
111 | excerpt: 🔒 The post has been encrypted and can only be viewed after entering the password.
112 | input_password: Please enter password ...
113 | re_encryption: Re-encryption
114 |
--------------------------------------------------------------------------------
/languages/zh-CN.yml:
--------------------------------------------------------------------------------
1 | # ====================================================================
2 | # 中文简体
3 | # ====================================================================
4 |
5 | category: 分类
6 | categories: 分类
7 | tag: 标签
8 | tags: 标签
9 | tagcloud: 标签云
10 | home: 首页
11 | archive: 归档
12 | archives: 归档
13 | about: 关于
14 | links: 友链
15 | link: 友链
16 | photos: 相册
17 | photo: 照片
18 | tool: 工具
19 | tools: 工具
20 | me: 我的
21 | search: 搜索...
22 | prev: 上一页
23 | next: 下一页
24 | prev_posts: 上一篇
25 | next_posts: 下一篇
26 | page: 第 %d 页
27 | recent_posts: 最新文章
28 | share: 分享
29 | theme: 主题
30 | rss_feed: RSS Feed
31 | top: 置顶
32 | read_more: 阅读全文
33 | wordcount: 字
34 | min2read: 分钟
35 | changelog: 更新日志
36 | social_contact: 社交
37 | github: GitHub
38 | google: 谷歌
39 | apple: 苹果
40 | microsoft: 微软
41 | weixin: 微信
42 | qq: QQ
43 | alipay: 支付宝
44 | weibo: 微博
45 | zhihu: 知乎
46 | twitter: 推特
47 | x: X
48 | facebook: Facebook
49 | email: Email
50 | code_block:
51 | copy: 复制代码
52 | copied: 已复制
53 | fold: 折叠代码块
54 | folded: 已折叠
55 | copy_copyright:
56 | copy: 复制版权信息
57 | copied: 已复制
58 | title: 原文标题
59 | author: 原文作者
60 | link: 原文链接
61 | copyright:
62 | title: 标题
63 | author: 作者
64 | link: 链接
65 | create_time: 创建时间
66 | published: 发布于
67 | license: 许可
68 | license_title: 版权声明
69 | license_content: "本博客所有文章除特别声明外,均采用 %s 许可协议。转载请注明出处!"
70 | ago:
71 | second: "%s 秒前"
72 | minute: "%s 分钟前"
73 | hour: "%s 小时前"
74 | day: "%s 天前"
75 | week: "%s 周前"
76 | month: "%s 个月前"
77 | year: "%s 年前"
78 | footer:
79 | powered_by: "由 %s 驱动"
80 | site_uv: 访客数
81 | site_pv: 访问量
82 | deploy: "本站由 %s 提供部署服务"
83 | deploy_provider:
84 | github: GitHub Pages
85 | vercel: Vercel
86 | netlify: Netlify
87 | cloudflare: Cloudflare
88 | gitee: Gitee Pages
89 | aliyun: 阿里云
90 | tencent_cloud: 腾讯云
91 | upyun: 又拍云
92 | total_words: 总字数
93 | post:
94 | aging: "本文距离上次更新已过去 %s 天,部分内容可能已经过时,请注意甄别。"
95 | share:
96 | qq: "分享到 QQ"
97 | wechat: "分享到微信"
98 | weibo: "分享到微博"
99 | wechat_scan: "微信扫一扫"
100 | reward: 打赏作者
101 | comment:
102 | comments: 评论
103 | fail_tip: 评论插件加载失败
104 | reload: 点击重新加载
105 | loading: 正在加载评论插件
106 | config_error: "请完整填写 %s 评论插件必需的配置项"
107 | 404:
108 | page_not_found: 页面找不到
109 | go_home: 前往首页
110 | encryption:
111 | excerpt: 🔒 文章已加密,输入密码后才能查看。
112 | input_password: 请输入密码...
113 | re_encryption: 重新加密
114 |
--------------------------------------------------------------------------------
/languages/zh-TW.yml:
--------------------------------------------------------------------------------
1 | # ====================================================================
2 | # 中文繁體
3 | # ====================================================================
4 |
5 | category: 分類
6 | categories: 分類
7 | tag: 標籤
8 | tags: 標籤
9 | tagcloud: 標籤雲
10 | home: 首頁
11 | archive: 歸檔
12 | archives: 歸檔
13 | about: 關於
14 | links: 友鏈
15 | link: 友鏈
16 | photos: 相簿
17 | photo: 照片
18 | tool: 工具
19 | tools: 工具
20 | me: 我的
21 | search: 搜尋...
22 | prev: 上一頁
23 | next: 下一頁
24 | prev_posts: 上一篇
25 | next_posts: 下一篇
26 | page: 第 %d 頁
27 | recent_posts: 之前的文章
28 | share: 分享
29 | theme: 主題
30 | rss_feed: RSS 訂閱
31 | top: 頂端
32 | read_more: 繼續閱讀...
33 | wordcount: 字數
34 | min2read: 分鐘
35 | changelog: 日誌
36 | social_contact: 社交
37 | github: GitHub
38 | google: 谷歌
39 | apple: 蘋果
40 | microsoft: 微軟
41 | weixin: 微信
42 | qq: QQ
43 | alipay: 支付寶
44 | weibo: 微博
45 | zhihu: 知乎
46 | twitter: Twitter
47 | x: X
48 | facebook: Facebook
49 | email: Email
50 | code_block:
51 | copy: 複製代碼
52 | copied: 已複製
53 | fold: 折疊代碼塊
54 | folded: 已折疊
55 | copy_copyright:
56 | copy: 複製版權資訊
57 | copied: 已複製
58 | title: 原文標題
59 | author: 原文作者
60 | link: 原文連結
61 | copyright:
62 | author: 作者
63 | title: 標題
64 | link: 連結
65 | create_time: 撰寫時間
66 | published: 發布於
67 | license: 版權
68 | license_title: 版權宣告
69 | license_content: "這個網站所有文章均使用 %s 授權"
70 | ago:
71 | second: "%s 秒前"
72 | minute: "%s 分鐘前"
73 | hour: "%s 小時前"
74 | day: "%s 天前"
75 | week: "%s 周前"
76 | month: "%s 個月前"
77 | year: "%s 年前"
78 | footer:
79 | powered_by: "%s 框架"
80 | site_uv: 讀者
81 | site_pv: 閱覽
82 | deploy: "本站由 %s 提供部署服務"
83 | deploy_provider:
84 | github: GitHub Pages
85 | vercel: Vercel
86 | netlify: Netlify
87 | cloudflare: Cloudflare
88 | gitee: Gitee Pages
89 | aliyun: 阿里雲
90 | tencent_cloud: 騰訊雲
91 | upyun: 又拍雲
92 | total_words: 總字數
93 | post:
94 | aging: "本文距離上次更新已過去 %s 天,部分內容可能已經過時,請註意甄別。"
95 | share:
96 | qq: "分享到 QQ"
97 | wechat: "分享到微信"
98 | weibo: "分享到微博"
99 | wechat_scan: "微信掃一掃"
100 | reward: 給作者打賞
101 | comment:
102 | comments: 留言
103 | fail_tip: 留言插件加載失敗
104 | reload: 點擊重新加載
105 | loading: 正在載入留言插件
106 | config_error: "請完整填寫 %s 留言插件的必填設定項"
107 | 404:
108 | page_not_found: 頁面缺失
109 | go_home: 前往首頁
110 | encryption:
111 | excerpt: 🔒 文章已加密,輸入密碼後才能查看。
112 | input_password: 請輸入密碼...
113 | re_encryption: 重新加密
114 |
--------------------------------------------------------------------------------
/layout/_page/404.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
404
4 |
5 | <%- __('404.page_not_found') %>
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/layout/_page/archive.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%- partial('_partial/archive-list', { archive_posts: page.posts }) %>
5 |
6 |
7 | <%- partial('_partial/paginator', { page_info: page }) %>
8 |
9 |
--------------------------------------------------------------------------------
/layout/_page/category-archive.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%= page.category %>
5 |
6 |
7 | <%- partial('_partial/archive-list', { archive_posts: page.posts }) %>
8 |
9 |
10 | <%- partial('_partial/paginator', { page_info: page }) %>
11 |
12 |
--------------------------------------------------------------------------------
/layout/_page/category.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | <% if (site.categories.length > 0) { %>
4 |
5 | <%- list_categories(site.categories, {
6 | class: 'site-all-category'
7 | }) %>
8 |
9 | <% } else { %>
10 | <%- partial('_partial/common/empty-content') %>
11 | <% } %>
12 | <% if (page?.comment === true) { %>
13 | <%- partial('_partial/comment/comment') %>
14 | <% } %>
15 |
16 |
17 |
--------------------------------------------------------------------------------
/layout/_page/home.ejs:
--------------------------------------------------------------------------------
1 |
2 | <% if (theme?.home?.announcement) { %>
3 |
4 |
5 |
6 | <%- markdown(theme?.home?.announcement) %>
7 |
8 |
9 |
10 | <% } %>
11 |
12 | <% page.posts.forEach(post => { %>
13 | -
14 |
15 | <% if (post?.home_cover) { %>
16 |
19 | <% if (post.sticky) { %>
20 |
21 | <%- __('top') %>
22 |
23 | <% } %>
24 |
 %>)
27 |
28 | <% } %>
29 |
30 |
31 | <% if (post.sticky && !post?.home_cover) { %>
32 |
33 | <%- __('top') %>
34 |
35 | <% } %>
36 |
41 |
42 |
43 | <% if (post?.password) { %>
44 | <%= __('encryption.excerpt') %>
45 | <% } else { %>
46 | <%- post?.excerpt || truncate(strip_html(post.content), { length: 128 }) %>
47 | <% } %>
48 |
49 |
50 | <%- partial('_partial/post/post-meta-info', { post: post, page_type: 'home' }) %>
51 |
52 |
53 | <% }) %>
54 |
55 | <%- partial('_partial/paginator', { page_info: page }) %>
56 |
57 |
--------------------------------------------------------------------------------
/layout/_page/tag-archive.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
<%= page.tag %>
4 |
5 |
6 | <%- partial('_partial/archive-list', { archive_posts: page.posts }) %>
7 |
8 |
9 | <%- partial('_partial/paginator', { page_info: page }) %>
10 |
11 |
--------------------------------------------------------------------------------
/layout/_page/tag.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | <% if (site.tags.length){ %>
4 | <% if (page?.tag_cloud === false){ %>
5 |
6 | <%- list_tags() %>
7 |
8 | <% } else { %>
9 |
10 | <%- tagcloud({
11 | min_font: 1,
12 | max_font: 1.6,
13 | unit: 'rem',
14 | amount: 100
15 | }) %>
16 |
17 | <% } %>
18 | <% } else { %>
19 | <%- partial('_partial/common/empty-content') %>
20 | <% } %>
21 | <% if (page?.comment === true) { %>
22 | <%- partial('_partial/comment/comment') %>
23 | <% } %>
24 |
25 |
26 |
--------------------------------------------------------------------------------
/layout/_partial/archive-list.ejs:
--------------------------------------------------------------------------------
1 | <% const archive_post_list = createNewArchivePosts(archive_posts) %>
2 |
3 | <% archive_post_list.forEach(postItem => { %>
4 |
5 |
8 |
9 | <% postItem.postList.forEach(post => { %>
10 | -
11 |
12 | <%= date(post.date, 'MM-DD') %>
13 | <%= post.title %>
16 |
17 | <% }) %>
18 |
19 |
20 | <% }) %>
21 |
22 |
--------------------------------------------------------------------------------
/layout/_partial/comment/comment.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const {
3 | enable: theme_comment_enable,
4 | use: theme_comment_use
5 | } = theme?.comment || {}
6 | %>
7 |
8 | <% if (theme_comment_enable === true && theme_comment_use) { %>
9 |
30 | <% } %>
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/layout/_partial/comment/config-error.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%- __('comment.config_error', comment_plugin) %>
4 |
5 |
--------------------------------------------------------------------------------
/layout/_partial/comment/load-handler.ejs:
--------------------------------------------------------------------------------
1 |
5 |
9 |
24 |
--------------------------------------------------------------------------------
/layout/_partial/comment/plugins/artalk.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const { enable: pjax_enable } = theme?.pjax || {}
3 | const {
4 | server: artalk_server_url,
5 | options: artalk_options = {}
6 | } = theme?.comment?.artalk || {}
7 | const artalk_language = config?.language === 'en' ? 'en-US' : 'zh-CN'
8 | const artalk_css = `${artalk_server_url}/dist/Artalk.css`
9 | const artalk_js = `${artalk_server_url}/dist/Artalk.js`
10 | %>
11 | <% if(artalk_server_url) { %>
12 |
70 | <% } else { %>
71 | <%- partial('../config-error', { comment_plugin: 'Artalk' }) %>
72 | <% } %>
73 |
--------------------------------------------------------------------------------
/layout/_partial/comment/plugins/disqus.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const { enable: pjax_enable } = theme?.pjax || {}
3 | const { shortname } = theme?.comment?.disqus || {}
4 | %>
5 | <% if (shortname) { %>
6 |
37 | <% } else { %>
38 | <%- partial('../config-error', { comment_plugin: 'Disqus' }) %>
39 | <% } %>
40 |
--------------------------------------------------------------------------------
/layout/_partial/comment/plugins/giscus.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const { enable: pjax_enable } = theme?.pjax || {}
3 | const {
4 | repo: giscus_repo,
5 | repo_id: giscus_repo_id,
6 | category: giscus_category,
7 | category_id: giscus_category_id,
8 | reactions_enabled: giscus_reactions_enabled,
9 | } = theme?.comment?.giscus || {}
10 | const giscus_language = config?.language || 'zh-CN'
11 | %>
12 | <% if (giscus_repo && giscus_repo_id && giscus_category && giscus_category_id) { %>
13 |
78 | <% } else { %>
79 | <%- partial('../config-error', { comment_plugin: 'Giscus' }) %>
80 | <% } %>
81 |
--------------------------------------------------------------------------------
/layout/_partial/comment/plugins/gitalk.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const { enable: pjax_enable } = theme?.pjax || {}
3 | const {
4 | client_id,
5 | client_secret,
6 | repository,
7 | github_id,
8 | github_admins,
9 | proxy
10 | } = theme?.comment?.gitalk || {}
11 | let cdn_css = '//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.css'
12 | let cdn_js = '//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.js'
13 | const { enable: t_cdn_enable, provider: t_cdn_provider } = theme?.cdn || {}
14 |
15 | if (t_cdn_enable === true) {
16 | switch (t_cdn_provider) {
17 | case 'jsdelivr':
18 | cdn_css = '//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.css'
19 | cdn_js = '//cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.js'
20 | break
21 |
22 | case 'unpkg':
23 | cdn_css = '//unpkg.com/gitalk/dist/gitalk.css'
24 | cdn_js = '//unpkg.com/gitalk/dist/gitalk.min.js'
25 | break
26 | }
27 | }
28 | %>
29 | <% if(client_id && client_secret && github_id && repository) { %>
30 |
78 | <% } else { %>
79 | <%- partial('../config-error', { comment_plugin: 'Gitalk' }) %>
80 | <% } %>
81 |
--------------------------------------------------------------------------------
/layout/_partial/comment/plugins/twikoo.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const { enable: pjax_enable } = theme?.pjax || {}
3 | let {
4 | env_id: tk_env_id,
5 | region: tk_region,
6 | version: tk_version
7 | } = theme?.comment?.twikoo || {}
8 | if (!tk_version) { tk_version = '1.6.36' }
9 | let twikoo_cdn_url = `//cdn.jsdelivr.net/npm/twikoo@${tk_version}/dist/twikoo.all.min.js`
10 | const { enable: t_cdn_enable, provider: t_cdn_provider } = theme?.cdn || {}
11 |
12 | if (t_cdn_enable === true) {
13 | switch (t_cdn_provider) {
14 | case 'jsdelivr':
15 | twikoo_cdn_url = `//cdn.jsdelivr.net/npm/twikoo@${tk_version}/dist/twikoo.all.min.js`
16 | break
17 |
18 | case 'unpkg':
19 | twikoo_cdn_url = `//unpkg.com/twikoo@${tk_version}/dist/twikoo.all.min.js`
20 | break
21 |
22 | case 'cdnjs':
23 | if (!(/^(\d+)\.(\d+)\.(\d+)$/.test(tk_version))) {
24 | tk_version = '1.6.36'
25 | }
26 | twikoo_cdn_url = `https://cdnjs.cloudflare.com/ajax/libs/twikoo/${tk_version}/twikoo.all.min.js`
27 | break
28 | }
29 | }
30 | %>
31 | <% if(tk_env_id) { %>
32 |
33 |
34 |
39 |
67 |
68 | <% } else { %>
69 | <%- partial('../config-error', { comment_plugin: 'Twikoo' }) %>
70 | <% } %>
71 |
--------------------------------------------------------------------------------
/layout/_partial/comment/plugins/valine.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const { enable: pjax_enable } = theme?.pjax || {}
3 | const {
4 | appid,
5 | appkey,
6 | server_urls,
7 | placeholder
8 | } = theme?.comment?.valine || {}
9 | let cdn_url = '//cdn.jsdelivr.net/npm/valine@latest/dist/Valine.min.js'
10 | const { enable: t_cdn_enable, provider: t_cdn_provider } = theme?.cdn || {}
11 |
12 | if (t_cdn_enable === true) {
13 | switch (t_cdn_provider) {
14 | case 'jsdelivr':
15 | cdn_url = '//cdn.jsdelivr.net/npm/valine@latest/dist/Valine.min.js'
16 | break
17 |
18 | case 'unpkg':
19 | cdn_url = '//unpkg.com/valine@latest/dist/Valine.min.js'
20 | break
21 | }
22 | }
23 | %>
24 | <% if(appid && appkey) { %>
25 |
26 |
27 |
32 |
70 |
71 | <% } else { %>
72 | <%- partial('../config-error', { comment_plugin: 'Valine' }) %>
73 | <% } %>
74 |
--------------------------------------------------------------------------------
/layout/_partial/common/empty-content.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/layout/_partial/common/mermaid.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const mermaidVersion = config?.mermaid?.version || '10.5.0'
3 | let mermaidJs = `//cdn.jsdelivr.net/npm/mermaid@${mermaidVersion}/dist/mermaid.min.js`
4 |
5 | const { enable: t_cdn_enable, provider: t_cdn_provider } = theme?.cdn || {}
6 | const { enable: pjax_enable } = theme?.pjax || {}
7 |
8 | switch (t_cdn_enable === true && t_cdn_provider) {
9 | case 'jsdelivr':
10 | mermaidJs = `//cdn.jsdelivr.net/npm/mermaid@${mermaidVersion}/dist/mermaid.min.js`
11 | break
12 |
13 | case 'unpkg':
14 | mermaidJs = `//unpkg.com/mermaid@${mermaidVersion}/dist/mermaid.min.js`
15 | break
16 | }
17 | %>
18 |
19 | <% if (pjax_enable === true) { %>
20 |
25 | <% } %>
26 |
27 |
28 |
--------------------------------------------------------------------------------
/layout/_partial/head.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | <%
8 | let title = page.title;
9 | if (is_archive()) {
10 | title = __('archive');
11 | if (is_month()) {
12 | title += ': ' + page.year + '/' + page.month;
13 | } else if (is_year()) {
14 | title += ': ' + page.year;
15 | }
16 | } else if (is_category()) {
17 | title = __('category') + ': ' + page.category;
18 | } else if (is_tag()) {
19 | title = __('tag') + ': ' + page.tag;
20 | } else {
21 | title = __(page.title);
22 | }
23 | %>
24 |
25 | <% if (title) { %>
26 | <%= title %> |
27 | <% } %>
28 | <%= theme?.base_info?.title || config?.title || 'Keep Theme' %>
29 |
30 | <%- css('css/style') %>
31 | <% if (theme?.base_info?.favicon) { %>
32 | <%- favicon_tag(theme?.base_info?.favicon) %>
33 | <% } %>
34 | <%- __css('font/css/fontawesome.min.css') %>
35 | <%- __css('font/css/regular.min.css') %>
36 | <%- __css('font/css/solid.min.css') %>
37 | <%- __css('font/css/brands.min.css') %>
38 | <% if (theme?.inject?.enable === true) { %>
39 | <% theme?.inject?.css?.forEach((css_path) => { %>
40 | <% if (css_path && isCssFile(css_path)) { %>
41 | <%- css(css_path) %>
42 | <% } %>
43 | <% }) %>
44 | <% } %>
45 | <%- exportThemeConfig() %>
46 |
47 |
48 |
--------------------------------------------------------------------------------
/layout/_partial/image-mask.ejs:
--------------------------------------------------------------------------------
1 |
2 |
![]()
3 |
4 |
--------------------------------------------------------------------------------
/layout/_partial/local-search.ejs:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
--------------------------------------------------------------------------------
/layout/_partial/paginator.ejs:
--------------------------------------------------------------------------------
1 | <% if (page.total > 1) { %>
2 |
3 |
6 |
7 |
12 |
13 |
14 |
15 | /<%- page.total %>
21 |
22 |
23 |
24 |
29 |
30 |
33 |
34 | <% } %>
35 |
36 |
37 |
--------------------------------------------------------------------------------
/layout/_partial/pjax/pjax.ejs:
--------------------------------------------------------------------------------
1 | <%- __js('js/libs/pjax.min.js') %>
2 |
30 |
--------------------------------------------------------------------------------
/layout/_partial/post/post-copyright-info.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | let license_lang = ''
3 | if (config?.language === 'zh-CN') { license_lang = '.zh-hans' }
4 | if (config?.language === 'zh-TW') { license_lang = '.zh-hant' }
5 | let license_url = 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed' + license_lang
6 |
7 | const use_custom = theme?.post?.copyright_info !== true && theme?.post?.copyright_info?.custom_license && theme?.post?.copyright_info?.custom_link
8 | if (use_custom) { license_url = theme?.post?.copyright_info?.custom_link }
9 | %>
10 |
11 |
12 |
13 |
14 | <%= page.title %>
15 |
16 |
17 |
18 | <%= page.path %>
19 |
20 |
21 |
22 |
23 |
24 |
25 | <%- __('copyright.author') %>
26 |
27 |
<%= theme?.base_info?.author || config?.author || 'Anonymous' %>
28 |
29 |
30 |
31 |
32 | <%- __('copyright.published') %>
33 |
34 |
<%= date(page.date, 'YYYY-MM-DD HH:mm') %>
35 |
36 |
37 |
38 |
39 |
40 | <%- __('copyright.license') %>
41 |
42 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/layout/_partial/post/post-share.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
6 |
7 |
8 | -
13 |
14 |
15 | -
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/layout/_partial/post/post-tools.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <% if (page?.password) { %>
5 | -
8 |
9 |
10 |
11 | <% } %>
12 |
13 |
14 | <% if (page?.toc !== false && theme?.toc?.enable === true) { %>
15 | -
16 |
17 |
18 | <% } %>
19 |
20 |
21 | <% if (page?.comment !== false && theme?.comment?.enable === true && theme?.comment?.use) { %>
22 |
26 | <% } %>
27 |
28 |
29 | -
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/layout/_partial/post/reward-author.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const { img_link, text, icon } = theme?.post?.reward || {}
3 | %>
4 |
5 |
6 |
11 | <%= (text || __('post.reward')).toUpperCase() %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/layout/_partial/progress-bar.ejs:
--------------------------------------------------------------------------------
1 |
2 | <% if (theme?.scroll?.progress_bar === true) { %>
3 |
4 | <% } %>
5 |
6 | <% if (theme?.pjax?.enable === true) { %>
7 |
8 |
9 | <% } %>
10 |
11 |
12 |
--------------------------------------------------------------------------------
/layout/_partial/scripts.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const { links: links_data, photos: photos_data, tools: tools_data } = theme?.source_data
3 | const page_template = page?.template?.toLowerCase()
4 | %>
5 |
6 |
7 | <%- __js([
8 | 'js/utils.js',
9 | 'js/header-shrink.js',
10 | 'js/back2top.js',
11 | 'js/toggle-theme.js',
12 | 'js/code-block.js',
13 | 'js/main.js',
14 | 'js/libs/anime.min.js'
15 | ]) %>
16 |
17 |
18 | <% if (theme?.local_search?.enable === true) { %>
19 | <%- __js('js/local-search.js') %>
20 | <% } %>
21 |
22 |
23 | <% if (theme?.lazyload?.enable === true) { %>
24 | <%- __js('js/lazyload.js') %>
25 | <% } %>
26 |
27 |
28 |
29 | <% if (is_home()) { %>
30 | <%- __js('js/page/home-page.js') %>
31 | <% } %>
32 |
33 |
34 | <% if (is_post()) { %>
35 |
36 | <%- __js('js/post/post-helper.js') %>
37 |
38 |
39 | <% if (theme?.toc?.enable === true) { %>
40 | <%- __js('js/post/toc.js') %>
41 | <% } %>
42 |
43 |
44 | <% if (theme?.post?.copyright_info === true) { %>
45 | <%- __js('js/post/copyright-info.js') %>
46 | <% } %>
47 |
48 |
49 | <% if (theme?.post?.share === true) { %>
50 | <%- __js('js/post/share.js') %>
51 | <% } %>
52 | <% } %>
53 |
54 |
55 | <% if (is_current('/categories') || is_current('/category')) { %>
56 | <%- __js('js/page/category-page.js') %>
57 | <% } %>
58 |
59 |
60 | <% if ((page_template === 'links' || page_template === 'link') && links_data) { %>
61 | <%- __js('js/page/links-page.js') %>
62 | <% } %>
63 |
64 |
65 | <% if ((page_template === 'photos' || page_template === 'photo') && photos_data) { %>
66 | <%- __js('js/page/photos-page.js') %>
67 | <% } %>
68 |
69 |
70 | <% if ((page_template === 'tools' || page_template === 'tool') && tools_data) { %>
71 | <%- __js('js/page/tools-page.js') %>
72 | <% } %>
73 |
74 |
75 |
76 | <% if (config?.mermaid?.enable === true) { %>
77 | <%- partial('common/mermaid') %>
78 | <% } %>
79 |
80 |
81 | <% if (theme?.pjax?.enable === true) { %>
82 | <%- partial('pjax/pjax') %>
83 | <% } %>
84 |
--------------------------------------------------------------------------------
/layout/_partial/side-tools.ejs:
--------------------------------------------------------------------------------
1 |
59 |
--------------------------------------------------------------------------------
/layout/_partial/toc.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%- toc(
4 | page.content,
5 | {
6 | class: 'nav',
7 | list_number: theme?.toc?.number || false
8 | }
9 | ) %>
10 |
11 |
12 |
--------------------------------------------------------------------------------
/layout/_template/friends-link.ejs:
--------------------------------------------------------------------------------
1 |
28 |
--------------------------------------------------------------------------------
/layout/_template/page-template.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const page_title = page?.title?.toLowerCase()
3 | const page_template = page?.template?.toLowerCase()
4 | const use_page_template = page?.use_template !== false
5 | const { links: links_data, photos: photos_data, tools: tools_data } = theme?.source_data
6 |
7 | let current_page_data = null
8 | let current_page_template = null
9 | let keep_container = 'keep-container'
10 |
11 | if (page_template === 'links' || page_template === 'link') {
12 | current_page_data = links_data
13 | current_page_template = 'friends-link'
14 | }
15 |
16 | if (page_template === 'photos' || page_template === 'photo') {
17 | current_page_data = photos_data
18 | current_page_template = 'photo-album'
19 | }
20 |
21 | if (page_template === 'tools' || page_template === 'tool') {
22 | current_page_data = tools_data
23 | current_page_template = 'tools-nav'
24 | if (current_page_data) { keep_container = '' }
25 | }
26 |
27 | const page_cover = page?.page_cover || ''
28 | const has_page_cover = page_cover ? 'has-page-cover' : ''
29 | const page_cover_title = page?.page_cover_title || ''
30 | const page_cover_height = page?.page_cover_height ? page?.page_cover_height + 'px' : '200px'
31 | %>
32 |
33 |
34 |
35 |
36 | <% if (page?.page_cover) { %>
37 |
40 |
 %>)
43 | <% if (page_cover_title) { %>
44 |
45 | <%= page_cover_title %>
46 |
47 | <% } %>
48 |
49 | <% } %>
50 |
51 |
52 |
53 |
54 | <% if (use_page_template && current_page_data && current_page_template) { %>
55 | <%- partial(current_page_template) %>
56 | <% } %>
57 |
58 |
59 |
60 | <% if (page?.content || current_page_data) { %>
61 | <%- page.content %>
62 | <% } else { %>
63 |
<%= page_title.toUpperCase() %>
64 | <% } %>
65 |
66 |
67 |
68 | <% if (page?.comment === true) { %>
69 | <%- partial('_partial/comment/comment') %>
70 | <% } %>
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/layout/_template/photo-album.ejs:
--------------------------------------------------------------------------------
1 |
2 | <% for (const img of theme.source_data?.photos) { %>
3 |
 %>)
7 | <% } %>
8 |
9 |
--------------------------------------------------------------------------------
/layout/_template/tools-nav.ejs:
--------------------------------------------------------------------------------
1 | <%
2 | const btoaStr = (str) => btoa(encodeURIComponent(str)).trim().replaceAll('=', '')
3 | const tools_data = theme.source_data.tools.map((x, i) => {
4 | if (x.category) {
5 | x.anchorId = btoaStr(x.category) + String(i)
6 | }
7 | return x
8 | })
9 | const tools_nav_data = theme.source_data.tools.filter((t) => t?.category)
10 | %>
11 |
63 |
--------------------------------------------------------------------------------
/layout/index.ejs:
--------------------------------------------------------------------------------
1 | <%- partial('page') %>
2 |
--------------------------------------------------------------------------------
/layout/layout.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | <%- partial('_partial/head') %>
4 |
5 | <%- body %>
6 | <%- partial('_partial/scripts') %>
7 | <% if (theme?.inject?.enable === true) { %>
8 | <% theme?.inject?.js?.forEach((js_path) => { %>
9 | <% if (js_path && isJsFile(js_path)) { %>
10 | <%- js({
11 | class: 'custom-inject-js',
12 | src: js_path,
13 | 'data-pjax': true
14 | }) %>
15 | <% } %>
16 | <% }) %>
17 | <% } %>
18 |
19 |
20 |
--------------------------------------------------------------------------------
/layout/page.ejs:
--------------------------------------------------------------------------------
1 | <%- partial('_partial/progress-bar') %>
2 | <%
3 | const page_title = page?.title?.toLowerCase()
4 | const page_type = page?.type
5 | %>
6 |
7 |
8 | <% if (is_home() && theme?.first_screen?.enable === true && !page.prev) { %>
9 | <%- partial('_partial/first-screen') %>
10 | <% } %>
11 |
12 |
13 |
14 |
15 | <%- partial('_partial/header') %>
16 |
17 |
18 |
19 |
20 |
21 | <% if (is_home()) { %>
22 |
23 | <%- partial('_page/home') %>
24 |
25 | <% } else if (is_archive()) { %>
26 |
27 | <%- partial('_page/archive') %>
28 |
29 | <% } else if (is_post()) { %>
30 |
31 | <%- partial('_page/post') %>
32 |
33 | <% } else if (is_category()) { %>
34 |
35 | <%- partial('_page/category-archive') %>
36 |
37 | <% } else if (is_tag()) { %>
38 |
39 | <%- partial('_page/tag-archive') %>
40 |
41 | <% } else if (page_title === 'category' || page_title === 'categories') { %>
42 |
43 | <%- partial('_page/category') %>
44 |
45 | <% } else if (page_title === 'tag' || page_title === 'tags') { %>
46 |
47 | <%- partial('_page/tag') %>
48 |
49 | <% } else if (page_type === '404') { %>
50 |
51 | <%- partial('_page/404') %>
52 |
53 | <% } else { %>
54 |
55 | <%- partial('_template/page-template') %>
56 |
57 | <% } %>
58 |
59 |
60 |
61 |
62 | <%- partial('_partial/footer') %>
63 |
64 |
65 |
66 |
67 | <% if (is_post()) { %>
68 |
69 | <%- partial('_partial/post/post-tools') %>
70 |
71 | <% } %>
72 |
73 |
74 |
75 | <%- partial('_partial/side-tools') %>
76 |
77 |
78 |
79 | <%- partial('_partial/image-mask') %>
80 |
81 |
82 | <% if (theme?.local_search?.enable === true) { %>
83 | <%- partial('_partial/local-search') %>
84 | <% } %>
85 |
86 |
87 | <% if (is_post() && page?.toc !== false && theme?.toc?.enable === true) { %>
88 |
89 |
90 | <%- partial('_partial/toc') %>
91 |
92 |
93 | <% } %>
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hexo-theme-keep",
3 | "version": "4.2.5",
4 | "private": false,
5 | "description": "A simple and light theme for Hexo.",
6 | "scripts": {
7 | "format": "prettier --write ./source/js/*.js ./scripts",
8 | "lint:style": "stylelint --fix ./source/css",
9 | "prepare": "husky install"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "git+ssh://git@github.com/XPoet/hexo-theme-keep.git"
14 | },
15 | "keywords": [
16 | "hexo",
17 | "theme",
18 | "hexo-theme",
19 | "simple",
20 | "elegant",
21 | "beautiful",
22 | "powerful"
23 | ],
24 | "author": "XPoet",
25 | "license": "AGPL-3.0",
26 | "bugs": {
27 | "url": "https://github.com/XPoet/hexo-theme-keep/issues"
28 | },
29 | "homepage": "https://github.com/XPoet/hexo-theme-keep#readme",
30 | "lint-staged": {
31 | "*.{styl}": "stylelint --fix",
32 | "*.{js}": "prettier --write"
33 | },
34 | "devDependencies": {
35 | "@commitlint/cli": "^17.7.1",
36 | "@commitlint/config-conventional": "^17.7.0",
37 | "commitizen": "^4.3.0",
38 | "cz-conventional-changelog": "^3.3.0",
39 | "husky": "^8.0.0",
40 | "lint-staged": "^13.2.3",
41 | "prettier": "^2.7.1",
42 | "stylelint": "^14.13.0",
43 | "stylelint-config-rational-order": "^0.1.2",
44 | "stylelint-stylus": "^0.17.0"
45 | },
46 | "config": {
47 | "commitizen": {
48 | "path": "./node_modules/cz-conventional-changelog"
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/scripts/events/404-page.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict'
4 |
5 | hexo.extend.generator.register('generate-404-page', function () {
6 | return {
7 | path: '404.html',
8 | layout: ['page'],
9 | data: {
10 | type: '404',
11 | title: '404'
12 | }
13 | }
14 | })
15 |
--------------------------------------------------------------------------------
/scripts/events/config-handle.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict'
4 |
5 | const themeRootHandle = (root) => {
6 | if (root === '/') {
7 | return ''
8 | }
9 |
10 | if (root.length > 1 && root.charAt(0) !== '/') {
11 | root = '/' + root
12 | }
13 |
14 | const lastIdx = root.length - 1
15 | if (root.charAt(lastIdx) === '/') {
16 | root = root.slice(0, lastIdx)
17 | }
18 |
19 | return root
20 | }
21 |
22 | hexo.on('generateBefore', function () {
23 | hexo.theme.config.root = themeRootHandle(this.config.root)
24 |
25 | if (hexo.locals.get) {
26 | const data = hexo.locals.get('data')
27 |
28 | if (data) {
29 | // theme config file data
30 | if (data._config) {
31 | hexo.theme.config = { ...hexo.theme.config, ...data._config }
32 | } else if (data.keep) {
33 | hexo.theme.config = { ...hexo.theme.config, ...data.keep }
34 | }
35 |
36 | hexo.theme.config.source_data = {}
37 |
38 | // friends link data
39 | if (data.links) {
40 | hexo.theme.config.source_data.links = data.links
41 | }
42 |
43 | // custom social contact icon data
44 | if (data.icons) {
45 | hexo.theme.config.source_data.icons = data.icons
46 | }
47 |
48 | // photo album data
49 | if (data.photos) {
50 | hexo.theme.config.source_data.photos = data.photos
51 | }
52 |
53 | // tools nav data
54 | if (data.tools) {
55 | hexo.theme.config.source_data.tools = data.tools
56 | }
57 | }
58 | }
59 | })
60 |
--------------------------------------------------------------------------------
/scripts/events/keep-info.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict'
4 |
5 | hexo.on('ready', () => {
6 | if (!/^(g|s)/.test(hexo.env.cmd)) return
7 | const { version } = require('../../package.json')
8 | hexo.log.info(`
9 | ------------------------------------------
10 | __ ___ _______ _______ .______
11 | | |/ / | ____|| ____|| _ \\
12 | | ' / | |__ | |__ | |_) |
13 | | < | __| | __| | ___/
14 | | . \\ | |____ | |____ | |
15 | |__|\\__\\ |_______||_______|| _|
16 |
17 | ------------------------------------------
18 | Keep version ${version}
19 | Documentation: https://keep-docs.xpoet.cn
20 | ------------------------------------------
21 | `)
22 | })
23 |
--------------------------------------------------------------------------------
/scripts/filters/encrypt-handle.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict'
4 |
5 | const crypto = require('crypto')
6 |
7 | function encrypt(text, key, iv) {
8 | const algorithm = 'aes-256-cbc'
9 | const cipher = crypto.createCipheriv(algorithm, Buffer.from(key, 'hex'), Buffer.from(iv, 'hex'))
10 | let encrypted = cipher.update(text, 'utf8', 'hex')
11 | encrypted += cipher.final('hex')
12 | return encrypted
13 | }
14 |
15 | hexo.extend.filter.register(
16 | 'after_post_render',
17 | function (data) {
18 | let { password } = data
19 | password = String(password).trim()
20 | if (password) {
21 | const secretKey = crypto.randomBytes(32).toString('hex') // 256-bit
22 | const iv = crypto.randomBytes(16).toString('hex') // 128-bit
23 | data.secretKey = secretKey
24 | data.iv = iv
25 | data.encryptedPassword = encrypt(password, secretKey, iv)
26 | data.encryptedContent = encrypt(data.content, secretKey, iv)
27 | }
28 | },
29 | 1
30 | )
31 |
--------------------------------------------------------------------------------
/scripts/filters/image-handle.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict'
4 |
5 | const isAddRootPath = (root, src) => {
6 | if (/^(https?:\/\/)/.test(src)) {
7 | return src
8 | } else {
9 | return root + src
10 | }
11 | }
12 |
13 | const regExpImg = /
]*)src="([^"]*)"([^>\/]*)\/?\s*>/gim
14 |
15 | hexo.extend.filter.register(
16 | 'before_post_render',
17 | function (data) {
18 | const theme = hexo.theme.config
19 | data.content = data.content.replace(regExpImg, function (match, attrBegin, src, attrEnd) {
20 | if (!src) return match
21 | return `
`
22 | })
23 | },
24 | 1
25 | )
26 |
27 | hexo.extend.filter.register(
28 | 'after_post_render',
29 | function (data) {
30 | const theme = hexo.theme.config
31 |
32 | data.content = data.content.replace(regExpImg, function (match, attrBegin, src, attrEnd) {
33 | if (!src) return match
34 | // image lazy load
35 | if (theme?.lazyload?.enable === true) {
36 | let hasAlt = false
37 | if (attrBegin.includes('alt="')) {
38 | hasAlt = true
39 | }
40 | return `
`
46 | } else {
47 | return `
`
48 | }
49 | })
50 | },
51 | 1
52 | )
53 |
--------------------------------------------------------------------------------
/scripts/filters/link-handle.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict'
4 |
5 | hexo.extend.filter.register(
6 | 'after_post_render',
7 | function (data) {
8 | const config = this.config
9 | const url = new URL(config.url)
10 | const siteHost = url.hostname || config.url
11 |
12 | // Match 'a' tags that don't contain html children.
13 | const regPureATag = /]*)href="([^"]*)"([^>]*)>([^<]*)<\/a>/gim
14 |
15 | data.content = data.content.replace(
16 | regPureATag,
17 | function (match, attrBegin, href, attrEnd, html) {
18 | if (!href) return match
19 |
20 | let link = ''
21 | try {
22 | link = new URL(href)
23 | } catch (e) {
24 | // Invalid url, e.g. Anchor link.
25 | return match
26 | }
27 |
28 | // Exit if the url has same host with `config.url`, which means isn't an external link.
29 | if (!link.protocol || link.hostname === siteHost) return match
30 |
31 | return `${html}`
32 | }
33 | )
34 | },
35 | 0
36 | )
37 |
--------------------------------------------------------------------------------
/scripts/helpers/export-config.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict'
4 |
5 | const url = require('url')
6 | const fs = require('fs')
7 | const path = require('path')
8 | const yaml = require('js-yaml')
9 |
10 | /**
11 | * Export theme config to js
12 | */
13 | hexo.extend.helper.register('exportThemeConfig', function () {
14 | const { config, theme } = this
15 |
16 | // -------------------------- export languages --------------------------
17 | const languageDir = path.join(__dirname, '../../languages')
18 | let file = fs.readdirSync(languageDir).find((v) => v === `${config.language}.yml`)
19 | file = languageDir + '/' + (file ? file : 'en.yml')
20 | let languageContent = fs.readFileSync(file, 'utf8')
21 | try {
22 | languageContent = yaml.load(languageContent)
23 | } catch (err) {
24 | console.log(err)
25 | }
26 | // -----------------------------------------------------------------------
27 |
28 | // ----------------------------- hexo config -----------------------------
29 | const hexoConfig = {
30 | hostname: url.parse(config.url).hostname || config.url,
31 | root: config?.root || '/',
32 | language: config.language
33 | }
34 |
35 | if (config.search) {
36 | hexoConfig.path = config.search.path
37 | }
38 | // -----------------------------------------------------------------------
39 |
40 | // ----------------------------- theme config ----------------------------
41 | const themeConfig = theme
42 | themeConfig.version = require('../../package.json').version
43 | // -----------------------------------------------------------------------
44 |
45 | return ``
53 | })
54 |
--------------------------------------------------------------------------------
/scripts/tags/button.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = () =>
4 | function (args, content) {
5 | const colorKeywords = ['info', 'primary', 'success', 'warning', 'danger']
6 | const sizeKeywords = ['small', 'default', 'large']
7 |
8 | let colorClass = 'info'
9 | if (args[0] && colorKeywords.includes(args[0])) {
10 | colorClass = args[0]
11 | }
12 |
13 | let sizeClass = 'default'
14 | if (args[1] && sizeKeywords.includes(args[1])) {
15 | sizeClass = args[1]
16 | }
17 |
18 | let attribute = ''
19 | if (args[2]) {
20 | attribute = `onclick="window.open('${args[2]}')"`
21 | }
22 |
23 | return ``
24 | }
25 |
--------------------------------------------------------------------------------
/scripts/tags/index.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict'
4 |
5 | const noteTag = require('./note')(hexo)
6 | hexo.extend.tag.register('note', noteTag, true)
7 |
8 | const buttonTag = require('./button')(hexo)
9 | hexo.extend.tag.register('button', buttonTag, true)
10 | hexo.extend.tag.register('btn', buttonTag, true)
11 |
12 | const tabsTag = require('./tabs')(hexo)
13 | hexo.extend.tag.register('tabs', tabsTag, true)
14 |
--------------------------------------------------------------------------------
/scripts/tags/note.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = (ctx) =>
4 | function (args, content) {
5 | const keywords = ['info', 'primary', 'success', 'warning', 'danger']
6 | let typeClassName = 'info'
7 |
8 | if (args[0] && keywords.includes(args[0])) {
9 | typeClassName = args[0]
10 | }
11 |
12 | let noteTitleContent = ''
13 | if (args[1]) {
14 | noteTitleContent = `${args[1]}
`
15 | }
16 |
17 | content = ctx.render.renderSync({ text: content, engine: 'markdown' })
18 |
19 | return `${noteTitleContent}${content}
`
20 | }
21 |
--------------------------------------------------------------------------------
/scripts/tags/tabs.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | module.exports = (ctx) =>
4 | function (args, content = '') {
5 | const tabsId = `keep-tabs-${Date.now()}`
6 | let activeTabIdx = Number(args[0]) || 1
7 |
8 | const tabBlock = /\n([\w\W\s\S]*?)/g
9 | const matches = content.matchAll(tabBlock)
10 |
11 | let tabIdx = 1
12 | let tabsNav = ''
13 | let tabsContent = ''
14 |
15 | for (const match of matches) {
16 | let tabName = match[1]
17 | const href = `${tabsId}-${tabIdx}`
18 | const isActive = tabIdx === activeTabIdx ? ' active' : ''
19 | tabIdx += 1
20 |
21 | tabsNav += `${tabName}
`
22 |
23 | let tabContent = match[2]
24 | tabContent = ctx.render.renderSync({ text: tabContent, engine: 'markdown' }).trim()
25 | tabsContent += `${tabContent}
`
26 | }
27 |
28 | tabsNav = `${tabsNav}
`
29 | tabsContent = `${tabsContent}
`
30 |
31 | return `
32 | ${tabsNav + tabsContent}
33 |
`
34 | }
35 |
--------------------------------------------------------------------------------
/source/css/common/animated.styl:
--------------------------------------------------------------------------------
1 | transition-g() {
2 | transition-delay 0s, 0s, 0s, 0s
3 | transition-timing-function ease, ease, ease, ease
4 | transition-duration 0.2s, 0.2s, 0.2s, 0.2s
5 | transition-property color, background, box-shadow, border-color
6 | }
7 |
8 |
9 | transition-t(property, delay, duration, function) {
10 | $temp-property = "color, background, box-shadow, border-color"
11 | $temp-delay = "0s, 0s, 0s, 0s"
12 | $temp-duration = "0.2s, 0.2s, 0.2s, 0.2s"
13 | $temp-function = "ease, ease, ease, ease"
14 |
15 | for p in convert(property) {
16 | $temp-property += ("," + p)
17 | }
18 |
19 | for d in convert(delay) {
20 | $temp-delay += ("," + d + "s")
21 | }
22 |
23 | for d in convert(duration) {
24 | $temp-duration += ("," + d + "s")
25 | }
26 |
27 | for f in convert(function) {
28 | $temp-function += ("," + f)
29 | }
30 |
31 | transition-delay convert($temp-delay)
32 | transition-timing-function convert($temp-function)
33 | transition-duration convert($temp-duration)
34 | transition-property convert($temp-property)
35 | }
36 |
37 |
38 | .fade-in-down-animation {
39 | animation-name fade-in-down
40 | animation-duration 1s
41 | animation-fill-mode both
42 | }
43 |
44 |
45 | .title-hover-animation {
46 | position relative
47 | display inline-block
48 | color var(--text-color-2)
49 | line-height 1.3
50 | vertical-align top
51 | border-bottom none
52 |
53 | &::before {
54 | position absolute
55 | bottom -4px
56 | left 0
57 | width 100%
58 | height 2px
59 | background-color var(--text-color-2)
60 | transform scaleX(0)
61 | visibility hidden
62 | content ""
63 | transition-t("visibility transform", "0, 0", "0.2, 0.2", "ease-in-out, ease-in-out")
64 | }
65 |
66 | &:hover::before {
67 | transform scaleX(1)
68 | visibility visible
69 | }
70 | }
71 |
72 |
73 | @keyframes fade-in-down {
74 | 0% {
75 | transform translateY(-50px)
76 | opacity 0
77 | }
78 |
79 | 100% {
80 | transform translateY(0)
81 | opacity 1
82 | }
83 | }
84 |
85 | @keyframes heartbeat-animate {
86 | 0%
87 | 100% {
88 | transform scale(1)
89 | }
90 |
91 | 10%
92 | 30% {
93 | transform scale(0.88)
94 | }
95 |
96 | 20%
97 | 40%
98 | 60%
99 | 80% {
100 | transform scale(1.08)
101 | }
102 |
103 | 50%
104 | 70% {
105 | transform scale(1.08)
106 | }
107 | }
108 |
109 | @keyframes img-loading-animation {
110 | to {
111 | transform rotate(1turn)
112 | }
113 | }
114 |
115 | @keyframes blink-caret {
116 | from
117 | to {
118 | opacity 0
119 | }
120 | 50% {
121 | opacity 1
122 | }
123 | }
124 |
125 | @keyframes shake {
126 | 0% {
127 | transform rotate(-5deg)
128 | }
129 | 25% {
130 | transform rotate(5deg)
131 | }
132 | 50% {
133 | transform rotate(-5deg)
134 | }
135 | 75% {
136 | transform rotate(5deg)
137 | }
138 | 100% {
139 | transform rotate(-5deg)
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/source/css/common/code-block/code-block.styl:
--------------------------------------------------------------------------------
1 | .highlight-container {
2 | position relative
3 | box-sizing border-box
4 | margin 1.4rem 0
5 |
6 | &.mac {
7 | margin 1.4rem 0 1.8rem 0
8 | box-shadow 0 0.8rem 2rem 0 rgba(0, 0, 0, 0.4)
9 |
10 | &:hover {
11 | .code-tools-box .copy {
12 | opacity 1
13 | }
14 | }
15 |
16 |
17 | .code-tools-box {
18 | justify-content flex-end
19 | padding 0.4rem 0.6rem 0.7rem 0.4rem
20 | background var(--mac-toolbar-background-color)
21 |
22 | &::before {
23 | position absolute
24 | left 0.8rem
25 | width 0.76rem
26 | height 0.76rem
27 | background #fc625d
28 | border-radius 50%
29 | box-shadow 1.3rem 0 #fdbc40, 2.6rem 0 #35cd4b
30 | content ''
31 | }
32 |
33 |
34 | &.folded {
35 | border-bottom-right-radius 0
36 | border-bottom-left-radius 0
37 |
38 | .copy {
39 | display none
40 | }
41 | }
42 |
43 |
44 | .code-lang {
45 | order 1
46 | color #bbb
47 | }
48 |
49 |
50 | .fold {
51 | order 2
52 | padding 0 0.1rem 0 0.6rem
53 |
54 | i {
55 | color #ccc
56 | }
57 | }
58 |
59 |
60 | .copy {
61 | position absolute
62 | top 3rem
63 | right 0.5rem
64 | box-sizing border-box
65 | padding 0 0.1rem
66 | opacity 0
67 | transition-t("opacity", "0", "0.2", "ease-in-out")
68 |
69 | i {
70 | font-size 1rem
71 | }
72 | }
73 | }
74 | }
75 |
76 |
77 | .code-tools-box {
78 | position relative
79 | z-index $z-index-1
80 | display flex
81 | align-items center
82 | justify-content space-between
83 | box-sizing border-box
84 | width 100%
85 | padding 0.3rem 0.4rem
86 | color var(--toolbar-foreground)
87 | background var(--toolbar-background)
88 | border-top-left-radius 0.3rem
89 | border-top-right-radius 0.3rem
90 |
91 |
92 | &.folded {
93 | border-bottom-right-radius 0.3rem
94 | border-bottom-left-radius 0.3rem
95 | }
96 |
97 |
98 | .code-lang {
99 | justify-content flex-start
100 | margin-left 0.2rem
101 | font-weight 600
102 | font-size 0.9rem
103 | font-family "Source Code Pro", consolas, Menlo
104 | if (hexo-config('code_block') && hexo-config('code_block.highlight_theme') == 'default') {
105 | color var(--text-color-3)
106 | }
107 | }
108 |
109 |
110 | .tool {
111 | disable-user-select()
112 | cursor pointer
113 |
114 | i {
115 | font-size 0.9rem
116 | if (hexo-config('code_block') && hexo-config('code_block.highlight_theme') == 'default') {
117 | color var(--text-color-3)
118 | }
119 | }
120 | }
121 |
122 |
123 | .fold {
124 | padding 0 0.4rem 0 0.2rem
125 | }
126 | }
127 |
128 |
129 | figure.highlight {
130 | margin 0
131 |
132 | &.folded {
133 | height 0 !important
134 | }
135 | }
136 | }
137 |
138 |
139 | figure.highlight {
140 | position relative
141 | box-sizing border-box
142 | overflow-y hidden
143 |
144 | transition-t("height", "0", "0.2", "linear")
145 |
146 | .shrink-line {
147 | position absolute
148 | left 0
149 | box-sizing border-box
150 | width 100%
151 | background linear-gradient(to bottom, var(--shrink-line-background-1), var(--shrink-line-background-2))
152 | cursor pointer
153 |
154 | &::before {
155 | color var(--text-color-4)
156 | font-weight 600
157 | font-size 1rem
158 | font-family 'Font Awesome 6 Free'
159 | content '\f103'
160 | }
161 |
162 | &:hover {
163 | &::before {
164 | font-size 1.1rem
165 | }
166 | }
167 | }
168 | }
169 |
170 |
171 |
--------------------------------------------------------------------------------
/source/css/common/css-variables.styl:
--------------------------------------------------------------------------------
1 | @import "stylus-variables.styl"
2 |
3 | :root {
4 | // ==============================================================================================
5 | // global font
6 | // ==============================================================================================
7 | --base-font-size 15.2px
8 | --base-line-height 22px
9 | --base-font-weight 400
10 | --base-font-family Optima-Regular, Optima, PingFang SC, Microsoft YaHei, sans-serif
11 |
12 |
13 | // ==============================================================================================
14 | // page
15 | // ==============================================================================================
16 | --page-content-width 80%
17 | --page-content-width-tablet 88%
18 | --page-content-width-mobile 90%
19 | --page-content-max-width 960px
20 | --page-content-max-width-2 calc(var(--page-content-max-width) * 1.2)
21 | --component-gap 36px
22 |
23 |
24 | // ==============================================================================================
25 | // box style
26 | // ==============================================================================================
27 | --box-border-radius 8px
28 |
29 |
30 | // ==============================================================================================
31 | // header
32 | // ==============================================================================================
33 | --header-height 70px
34 | --header-shrink-height calc(var(--header-height) * 0.72)
35 | --header-backdrop-filter-blur 4px
36 | --header-scroll-progress-bar-height 2px
37 | --header-title-font-family var(--base-font-family)
38 | --header-title-font-size 1.8rem
39 | --header-menu-icon inline-flex // Option values: inline-flex | none
40 |
41 |
42 | // ==============================================================================================
43 | // first screen
44 | // ==============================================================================================
45 | --first-screen-display block // Option values: block | none
46 |
47 | --first-screen-font-family var(--base-font-family)
48 | --first-screen-font-size 2rem
49 | --first-screen-font-color-light $text-color-3
50 | --first-screen-font-color-dark $dark-text-color-3
51 | --first-screen-font-offset 0
52 |
53 | --first-screen-icon-size 1.8rem
54 | --first-screen-icon-color-light $text-color-3
55 | --first-screen-icon-color-dark $dark-text-color-3
56 |
57 | --first-screen-header-font-color-light $text-color-3
58 | --first-screen-header-font-color-dark $dark-text-color-3
59 |
60 |
61 | // ==============================================================================================
62 | // home page
63 | // ==============================================================================================
64 | --home-post-hover-scale 1
65 |
66 |
67 | // ==============================================================================================
68 | // post page
69 | // ==============================================================================================
70 | --post-title-align left // Option values: left | center | right
71 | --post-author-avatar block // Option values: none | block
72 | --post-author-name flex // Option values: none | flex
73 | --post-create-datetime flex // Option values: none | flex
74 | --post-update-datetime flex // Option values: none | flex
75 | --post-img-align 0 auto 0 0 // Option values: 0 auto 0 0 (left) | 0 auto (center) | 0 0 0 auto (right)
76 | }
77 |
--------------------------------------------------------------------------------
/source/css/common/tags/keep-button.styl:
--------------------------------------------------------------------------------
1 | .keep-button {
2 | position relative
3 | box-sizing border-box
4 | margin auto 0.6rem
5 | padding 0.6rem 1rem
6 | color var(--text-color-3)
7 | font-size 1rem
8 | background var(--background-color-1)
9 | border none
10 | border-radius 0.4rem
11 | box-shadow 0.1rem 0.2rem 0.4rem var(--shadow-color)
12 | cursor pointer
13 |
14 |
15 | i {
16 | color var(--text-color-3)
17 | }
18 |
19 |
20 | &:hover {
21 | color var(--background-color-1)
22 | background var(--primary-color)
23 |
24 | i {
25 | color var(--background-color-1)
26 | }
27 | }
28 |
29 |
30 | &.size-small {
31 | margin auto 0.3rem
32 | padding 0.5rem 0.8rem
33 | font-size 0.8rem
34 | border-radius 0.3rem
35 | box-shadow 0.1rem 0.1rem 0.3rem var(--shadow-color)
36 | }
37 |
38 |
39 | &.size-large {
40 | width 100%
41 | margin 0.6rem 0
42 | padding 1rem
43 | font-size 1.1rem
44 | border-radius 0.5rem
45 | box-shadow 0.1rem 0.2rem 0.5rem var(--shadow-color)
46 | }
47 |
48 |
49 | &.color-info {
50 | color var(--text-color-3)
51 | background-color var(--background-color-1)
52 |
53 | &:hover {
54 | color var(--background-color-1)
55 | background var(--primary-color)
56 | }
57 | }
58 |
59 |
60 | &.color-primary {
61 | color var(--keep-primary-color)
62 | background-color var(--keep-primary-background-color)
63 |
64 | &:hover {
65 | color #fff
66 | background-color var(--keep-primary-color)
67 | }
68 | }
69 |
70 |
71 | &.color-success {
72 | color var(--keep-success-color)
73 | background-color var(--keep-success-background-color)
74 |
75 | &:hover {
76 | color #fff
77 | background-color var(--keep-success-color)
78 | }
79 | }
80 |
81 |
82 | &.color-warning {
83 | color var(--keep-warning-color)
84 | background-color var(--keep-warning-background-color)
85 |
86 | &:hover {
87 | color #fff
88 | background-color var(--keep-warning-color)
89 | }
90 | }
91 |
92 |
93 | &.color-danger {
94 | color var(--keep-danger-color)
95 | background-color var(--keep-danger-background-color)
96 |
97 | &:hover {
98 | color #fff
99 | background-color var(--keep-danger-color)
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/source/css/common/tags/keep-note.styl:
--------------------------------------------------------------------------------
1 | .keep-note {
2 | position relative
3 | box-sizing border-box
4 | width 100%
5 | margin-bottom 2rem
6 | padding 0.8rem 1rem
7 | font-size 0.9rem
8 | border-style solid
9 | border-width 0.1rem
10 | border-radius 0.4rem
11 |
12 | &.info {
13 | color var(--keep-info-color)
14 | background-color var(--keep-info-background-color)
15 | border-color var(--keep-info-border-color)
16 | }
17 |
18 |
19 | &.primary {
20 | color var(--keep-primary-color)
21 | background-color var(--keep-primary-background-color)
22 | border-color var(--keep-primary-border-color)
23 | }
24 |
25 |
26 | &.success {
27 | color var(--keep-success-color)
28 | background-color var(--keep-success-background-color)
29 | border-color var(--keep-success-border-color)
30 | }
31 |
32 |
33 | &.warning {
34 | color var(--keep-warning-color)
35 | background-color var(--keep-warning-background-color)
36 | border-color var(--keep-warning-border-color)
37 | }
38 |
39 |
40 | &.danger {
41 | color var(--keep-danger-color)
42 | background-color var(--keep-danger-background-color)
43 | border-color var(--keep-danger-border-color)
44 | }
45 |
46 |
47 | .keep-note-title {
48 | box-sizing border-box
49 | padding 0.6rem 0 0 0
50 | color inherit
51 | font-weight 600
52 | font-size 1rem
53 | }
54 |
55 |
56 | p {
57 | color inherit
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/source/css/common/tags/keep-tabs.styl:
--------------------------------------------------------------------------------
1 | $border-height = 2px
2 |
3 | .keep-tabs {
4 | position relative
5 | box-sizing border-box
6 | width 100%
7 | height auto
8 | background var(--background-color)
9 | border-radius 0.4rem
10 | box-shadow 0.1rem 0.1rem 0.5rem var(--shadow-color)
11 |
12 | .tabs-nav {
13 | position relative
14 | display flex
15 | justify-content flex-start
16 | box-sizing border-box
17 | list-style none
18 |
19 | &::before {
20 | position absolute
21 | bottom 0
22 | left 0
23 | box-sizing border-box
24 | width 100%
25 | height $border-height
26 | background var(--border-color)
27 | content ''
28 | }
29 |
30 | .tab {
31 | position relative
32 | box-sizing border-box
33 | margin-right 0.8rem
34 | padding 1rem 0.6rem
35 | overflow hidden
36 | color var(--text-color-3)
37 | cursor pointer
38 |
39 | &.active {
40 | font-weight 600
41 |
42 | &::before {
43 | position absolute
44 | bottom 0
45 | left 50%
46 | box-sizing border-box
47 | width 100%
48 | height $border-height
49 | background var(--primary-color)
50 | border-radius 0.2rem
51 | transform translateX(-50%)
52 | content ''
53 | }
54 | }
55 | }
56 | }
57 |
58 |
59 | .tabs-content {
60 | position relative
61 | box-sizing border-box
62 |
63 | .tab-pane {
64 | position relative
65 | box-sizing border-box
66 | width 100%
67 | height auto
68 | min-height 10rem
69 | padding 0.6rem 0.8rem
70 |
71 | &:not(.active) {
72 | display none
73 | }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/source/css/layout/_page/404.styl:
--------------------------------------------------------------------------------
1 | .error-404-container {
2 | disable-user-select()
3 |
4 | .text-1 {
5 | width 100%
6 | height 8rem
7 | color var(--text-color-3)
8 | font-weight 600
9 | font-size 8rem
10 | }
11 |
12 | .text-2 {
13 | width 100%
14 | height 3rem
15 | color var(--text-color-4)
16 | font-size 2rem
17 | letter-spacing 1.2px
18 | }
19 |
20 | .text-3 {
21 | width 100%
22 | height 4rem
23 |
24 | .go-home {
25 | position relative
26 | box-sizing border-box
27 | padding 0.3rem 0.8rem
28 | color var(--text-color-3)
29 | font-size 1rem
30 | background transparent
31 | border-color var(--text-color-3)
32 | border-style solid
33 | border-width 0.1rem
34 | border-radius 0.6rem
35 | cursor pointer
36 |
37 | &:hover {
38 | color var(--primary-color)
39 | border-color var(--primary-color)
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/source/css/layout/_page/archive.styl:
--------------------------------------------------------------------------------
1 | .archives-container {
2 | keep-container(1, 2rem, 2rem)
3 | }
4 |
5 |
6 |
--------------------------------------------------------------------------------
/source/css/layout/_page/category-archive.styl:
--------------------------------------------------------------------------------
1 | $category-name-font-size = 1.5rem
2 |
3 | .category-archive-container {
4 |
5 | keep-container(1, 2rem, 2rem)
6 |
7 | .category-name {
8 | margin-bottom var(--component-gap)
9 | color var(--text-color-2)
10 | font-weight 600
11 | font-size $category-name-font-size
12 |
13 | i {
14 | color var(--text-color-2)
15 | }
16 |
17 | +keep-tablet() {
18 | font-size $category-name-font-size * 0.9
19 | }
20 |
21 | +keep-mobile() {
22 | font-size $category-name-font-size * 0.8
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/source/css/layout/_page/category.styl:
--------------------------------------------------------------------------------
1 | $left-gap = 1.6rem
2 | $spacing-padding = 0.6rem
3 |
4 | .category-page-container {
5 |
6 | keep-container(1, 2rem, 0)
7 |
8 | padding-top 1.2rem
9 | padding-bottom 1.2rem
10 |
11 | .category-list-content {
12 |
13 | ul.site-all-category-list {
14 | user-select none
15 |
16 | & > li.site-all-category-list-item {
17 | position relative
18 | box-sizing border-box
19 | margin-bottom 0.8rem
20 | font-size 1rem
21 |
22 | &:last-child {
23 | margin-bottom 0
24 | }
25 |
26 |
27 | .self-category-info {
28 | display flex
29 | align-items center
30 | justify-content space-between
31 | height 2.6rem
32 |
33 | .left {
34 | display flex
35 | align-items center
36 | justify-content flex-start
37 | width 100%
38 | height 100%
39 |
40 |
41 | .icon {
42 | display flex
43 | flex-shrink 0
44 | align-items center
45 | justify-content flex-start
46 | width $left-gap
47 | height 100%
48 | color var(--text-color-3)
49 | font-size 1.06rem
50 | cursor pointer
51 | }
52 |
53 |
54 | .site-all-category-list-link {
55 | position relative
56 | display flex
57 | align-items center
58 | justify-content flex-start
59 | box-sizing border-box
60 | width 100%
61 | height 100%
62 | font-size 1.02rem
63 | }
64 | }
65 |
66 |
67 | .right {
68 | display flex
69 | flex-shrink 0
70 | align-items center
71 | justify-content flex-end
72 | width 1.6rem
73 | height 100%
74 |
75 | .site-all-category-list-count {
76 | color var(--text-color-3)
77 | font-size 1.08rem
78 | }
79 | }
80 | }
81 |
82 |
83 | .site-all-category-list-child {
84 | position relative
85 | box-sizing border-box
86 | height 0
87 | margin-left $left-gap
88 | overflow hidden
89 | visibility hidden
90 | }
91 | }
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/source/css/layout/_page/tag-archive.styl:
--------------------------------------------------------------------------------
1 | $tag-name-font-size = 1.5rem
2 |
3 | .tag-archive-container {
4 |
5 | keep-container(1, 2rem, 2rem)
6 |
7 | .tag-name {
8 | margin-bottom var(--component-gap)
9 | color var(--text-color-2)
10 | font-weight 600
11 | font-size $tag-name-font-size
12 |
13 | i {
14 | color var(--text-color-2)
15 | }
16 |
17 | +keep-tablet() {
18 | font-size $tag-name-font-size * 0.9
19 | }
20 |
21 | +keep-mobile() {
22 | font-size $tag-name-font-size * 0.8
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/source/css/layout/_page/tag.styl:
--------------------------------------------------------------------------------
1 | .tag-page-container {
2 |
3 | keep-container(1, 2rem, 0)
4 |
5 | .tagcloud-content {
6 |
7 | text-align justify
8 |
9 | a {
10 | display inline-block
11 | box-sizing border-box
12 | padding 0.68rem 0.5rem
13 | }
14 | }
15 |
16 |
17 | .tag-list-content {
18 | .tag-list {
19 | position relative
20 | display flex
21 | flex-wrap wrap
22 | gap 0.82rem
23 | justify-content flex-start
24 | box-sizing border-box
25 |
26 | .tag-list-item {
27 | position relative
28 | box-sizing border-box
29 | overflow hidden
30 | border-radius 0.3rem
31 |
32 |
33 | .tag-list-link {
34 | display inline-block
35 | padding 0.4rem 0.6rem
36 | color var(--text-color-3)
37 | background var(--background-color-2)
38 |
39 | &:hover {
40 | color var(--primary-color)
41 | }
42 | }
43 |
44 |
45 | .tag-list-count {
46 | display inline-block
47 | padding 0.4rem 0.6rem
48 | color var(--text-color-3)
49 | background var(--background-color-3)
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/archive-list.styl:
--------------------------------------------------------------------------------
1 | $archive-year-font-size = 1.6rem
2 | $post-title-font-size = 1.2rem
3 | $post-date-font-size = 1rem
4 | $post-date-width = 3.6rem
5 | $post-item-padding = 1.2rem
6 | $timeline-circle-size = 0.5rem
7 |
8 | .archive-list-container {
9 |
10 | .archive-item {
11 | margin-bottom var(--component-gap)
12 |
13 | &:last-child {
14 | margin-bottom 0
15 | }
16 |
17 | .archive-item-header {
18 | margin-bottom 0.8rem
19 |
20 | .archive-year {
21 | margin-right 6px
22 | color var(--text-color-2)
23 | font-weight 600
24 | font-size $archive-year-font-size
25 |
26 | +keep-tablet() {
27 | font-size $archive-year-font-size * 0.9
28 | }
29 | }
30 | }
31 |
32 |
33 | .archive-item-body {
34 | padding-left $post-item-padding
35 |
36 | .post-item {
37 | display flex
38 | align-items center
39 | justify-content space-between
40 | width 100%
41 | padding ($post-item-padding * 0.8) 0 ($post-item-padding * 0.8) $post-item-padding * 1.6
42 | font-size 1rem
43 |
44 | .starting-point {
45 | position absolute
46 | top 0
47 | left 0
48 | width 1rem
49 | height 100%
50 |
51 |
52 | &::after {
53 | position absolute
54 | top 0
55 | left 50%
56 | z-index $z-index-1
57 | box-sizing border-box
58 | width 0
59 | height 100%
60 | border-left 1px dashed var(--text-color-5)
61 | transform translateX(-50%)
62 | content ''
63 | }
64 |
65 |
66 | &::before {
67 | top 50%
68 | left 0
69 | z-index $z-index-2
70 | box-sizing border-box
71 | width $timeline-circle-size
72 | height $timeline-circle-size
73 | background var(--text-color-5)
74 | border-radius 50%
75 | content ''
76 | transition-t("height", "0", "0.2", "ease")
77 | }
78 | }
79 |
80 |
81 | &:hover {
82 | .starting-point {
83 | &::before {
84 | width $timeline-circle-size * 1.2
85 | height $timeline-circle-size * 1.2
86 | background var(--text-color-4)
87 | }
88 | }
89 |
90 | a.post-title {
91 | color var(--primary-color)
92 | }
93 | }
94 |
95 |
96 | .post-date {
97 | width $post-date-width
98 | margin-right $post-item-padding
99 | color var(--text-color-3)
100 | font-size $post-date-font-size
101 | }
102 |
103 |
104 | a.post-title {
105 | width 'calc(100% - %s)' % $post-date-width
106 | color var(--text-color-3)
107 | }
108 | }
109 | }
110 | }
111 | }
112 |
113 |
114 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/comment/comment.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('comment') && hexo-config('comment.enable') == true && hexo-config('comment.use')) {
2 | if (hexo-config('comment.use') == "valine") {
3 | @import "./valine.styl"
4 | }
5 | else if (hexo-config('comment.use') == "gitalk") {
6 | @import "./gitalk.styl"
7 | }
8 | else if (hexo-config('comment.use') == "twikoo") {
9 | @import "./twikoo.styl"
10 | }
11 | else if (hexo-config('comment.use') == "waline") {
12 | @import "./waline.styl"
13 | }
14 | }
15 |
16 | .comments-container {
17 | display inline-block
18 | width 100%
19 | margin-top var(--component-gap)
20 |
21 | .comment-area-title {
22 | width 100%
23 | color var(--text-color-3)
24 | font-size 1.38rem
25 | line-height 2
26 |
27 | i {
28 | color var(--text-color-3)
29 | }
30 |
31 | +keep-tablet() {
32 | font-size 1.2rem
33 | }
34 | }
35 |
36 |
37 | .configuration-items-error-tip {
38 | display flex
39 | align-items center
40 | margin-top 1rem
41 | color var(--text-color-3)
42 | font-size 1rem
43 |
44 | i {
45 | margin-right 0.3rem
46 | color var(--text-color-3)
47 | font-size 1.2rem
48 | }
49 | }
50 |
51 | .comment-plugin-fail {
52 | display none
53 | flex-direction column
54 | align-items center
55 | justify-content space-around
56 | width 100%
57 | padding 2rem
58 |
59 | .fail-tip {
60 | color var(--text-color-3)
61 | font-size 1.1rem
62 | }
63 |
64 | .reload {
65 | margin-top 1rem
66 | }
67 | }
68 |
69 | .comment-plugin-loading {
70 | flex-direction column
71 | padding 1rem
72 | color var(--text-color-3)
73 |
74 | .loading-icon {
75 | color var(--text-color-4)
76 | font-size 2rem
77 | }
78 |
79 | .load-tip {
80 | margin-top 1rem
81 | color var(--text-color-4)
82 | font-size 1.1rem
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/comment/gitalk.styl:
--------------------------------------------------------------------------------
1 | .gitalk-comment-container {
2 |
3 | transition-t("color, background", "0, 0", "0.2, 0.2", "ease, ease")
4 |
5 |
6 | .gt-container {
7 |
8 | .gt-btn {
9 | background-color var(--primary-color-light-1)
10 | border 1px solid var(--primary-color-light-1)
11 | transition-t("color, background", "0, 0", "0.2, 0.2", "ease, ease")
12 | }
13 |
14 |
15 | .gt-initing-text {
16 | color var(--text-color-3)
17 | }
18 |
19 |
20 | .gt-meta {
21 | .gt-counts {
22 | a {
23 | color var(--primary-color)
24 | }
25 | color var(--text-color-3)
26 | }
27 |
28 | .gt-user {
29 | .gt-user-name {
30 | color var(--text-color-3)
31 | }
32 |
33 | svg {
34 | path {
35 | fill var(--text-color-3)
36 | }
37 | }
38 | }
39 |
40 | .gt-popup {
41 | top 2.6rem
42 | background var(--background-color-2)
43 | border 1px solid var(--border-color)
44 | }
45 | }
46 |
47 |
48 | .gt-header {
49 | .gt-header-comment {
50 | textarea {
51 | color var(--text-color-3)
52 | background-color var(--background-color-2)
53 | transition-t("color, background", "0, 0", "0.2, 0.2", "ease, ease")
54 | }
55 |
56 | .gt-header-controls {
57 |
58 | .gt-btn-preview {
59 | color #fff
60 | background-color var(--primary-color-light-1)
61 | }
62 | }
63 | }
64 | }
65 |
66 |
67 | .gt-comments {
68 | .gt-comments-null {
69 | color var(--text-color-4)
70 | }
71 |
72 | .gt-comment-content {
73 | background-color var(--background-color-2)
74 | transition-t("color, background", "0, 0", "0.2, 0.2", "ease, ease")
75 |
76 | &:hover {
77 | box-shadow none
78 | }
79 |
80 | .gt-comment-body {
81 | p
82 | li {
83 | color var(--text-color-3)
84 | }
85 |
86 | .email-fragment {
87 | color var(--text-color-3)
88 | }
89 | }
90 |
91 | .notranslate {
92 | background-color var(--background-color-3)
93 |
94 | code {
95 | color var(--text-color-2)
96 | }
97 | }
98 |
99 | pre {
100 | background-color var(--background-color-3)
101 | }
102 | }
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/comment/twikoo.styl:
--------------------------------------------------------------------------------
1 | .twikoo-container {
2 |
3 | #twikoo {
4 |
5 | .tk-meta-input {
6 | .el-input {
7 | .el-input-group__prepend {
8 | color var(--text-color-3)
9 | }
10 |
11 | input {
12 | color var(--text-color-3)
13 | }
14 | }
15 | }
16 |
17 |
18 | .tk-input {
19 | textarea {
20 | color var(--text-color-3)
21 | }
22 | }
23 |
24 |
25 |
26 | .actions {
27 |
28 | .tk-row-actions-start {
29 |
30 | .tk-submit-action-icon {
31 |
32 | svg {
33 | path {
34 | fill var(--text-color-3)
35 | transition-t("fill", "0", "0.2", "ease")
36 | }
37 | }
38 |
39 | .OwO-body {
40 | color var(--text-color-3)
41 | background-color var(--background-color-1)
42 | }
43 | }
44 | }
45 |
46 | .tk-preview {
47 | span {
48 | color var(--text-color-3)
49 | background var(--background-color-2)
50 | }
51 | }
52 | }
53 |
54 |
55 | .tk-preview-container {
56 | color var(--text-color-3)
57 | }
58 |
59 |
60 | .tk-comments-container {
61 | .tk-comments-count {
62 | span {
63 | color var(--text-color-3)
64 | }
65 | }
66 | }
67 |
68 |
69 | .tk-main {
70 | margin-bottom 1.4rem
71 |
72 | .tk-meta {
73 |
74 | .tk-nick {
75 | color var(--text-color-3)
76 | }
77 |
78 | strong {
79 | color var(--text-color-3)
80 | }
81 |
82 |
83 | .tk-tag {
84 |
85 | &.tk-tag-green {
86 | color #fff
87 | background -webkit-linear-gradient(45deg, #e3565e, #ee854b, #f6c258, #90c68a, #5fb3b3, #6699cc, #c594c5)
88 | background linear-gradient(45deg, #e3565e, #ee854b, #f6c258, #90c68a, #5fb3b3, #6699cc, #c594c5)
89 | border none
90 | border-radius 0.4rem
91 | }
92 | }
93 |
94 |
95 | .tk-time {
96 | time {
97 | color var(--text-color-4)
98 | }
99 | }
100 | }
101 |
102 |
103 | .tk-content {
104 | color var(--text-color-3)
105 |
106 | span {
107 | color var(--text-color-3)
108 | }
109 |
110 | p {
111 | color var(--text-color-3)
112 | }
113 | }
114 |
115 | .tk-expand {
116 | color var(--text-color-4)
117 | }
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/comment/valine.styl:
--------------------------------------------------------------------------------
1 | .valine-container {
2 |
3 | #vcomments {
4 |
5 | .vwrap {
6 |
7 | border 0.1rem solid var(--text-color-4)
8 |
9 | .vheader {
10 |
11 | .vinput {
12 |
13 | color var(--text-color-3)
14 | border-color var(--text-color-4)
15 |
16 | &:focus {
17 | border-bottom 0.1rem dashed var(--primary-color) !important
18 | }
19 | }
20 | }
21 |
22 |
23 | .vedit {
24 |
25 | .veditor {
26 | color var(--text-color-3)
27 | }
28 |
29 | .vctrl {
30 |
31 | .vicon {
32 |
33 | fill var(--text-color-3)
34 |
35 | &.actived {
36 | fill var(--primary-color)
37 | }
38 | }
39 | }
40 | }
41 |
42 |
43 | button.vsubmit {
44 | color var(--text-color-3) !important
45 | background transparent !important
46 | border 0.1rem solid var(--text-color-3) !important
47 |
48 | &:hover {
49 | color var(--primary-color) !important
50 | border 0.1rem solid var(--primary-color) !important
51 | }
52 | }
53 | }
54 |
55 |
56 | .vcount {
57 | color var(--text-color-3)
58 |
59 | .vnum {
60 | color var(--text-color-2)
61 | }
62 | }
63 |
64 |
65 | .vcard {
66 |
67 | .vnick {
68 | .author {
69 | margin-left 0.15rem
70 | padding 0.15rem
71 | color #fff
72 | font-weight 450
73 | font-size 0.9rem
74 | background -webkit-linear-gradient(45deg, #e3565e, #ee854b, #f6c258, #90c68a, #5fb3b3, #6699cc, #c594c5)
75 | background linear-gradient(45deg, #e3565e, #ee854b, #f6c258, #90c68a, #5fb3b3, #6699cc, #c594c5)
76 | border-radius 0.15rem
77 | }
78 | }
79 |
80 |
81 | .vhead {
82 |
83 | .vnick {
84 | color var(--primary-color)
85 | }
86 |
87 | .vsys {
88 | color var(--text-color-3)
89 | background var(--background-color-2) !important
90 | }
91 | }
92 |
93 |
94 | .vcontent {
95 | P {
96 | color var(--text-color-3)
97 |
98 | code {
99 | color var(--code-foreground)
100 | background var(--code-background)
101 | }
102 | }
103 | }
104 |
105 |
106 | .vh {
107 | border-bottom-color var(--border-color)
108 | }
109 |
110 | .vquote {
111 | border-left-color var(--border-color)
112 | }
113 | }
114 |
115 |
116 | .vcopy {
117 | color var(--text-color-4)
118 | }
119 |
120 |
121 | .vpage {
122 |
123 | .vmore {
124 | color var(--text-color-2)
125 | border 0.1rem solid var(--border-color)
126 | }
127 | }
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/comment/waline.styl:
--------------------------------------------------------------------------------
1 | .waline-comment-container {
2 | #waline-comment {
3 | --waline-theme-color var(--primary-color)
4 | --waline-active-color var(--primary-color-light-1)
5 | --waline-color var(--text-color-3)
6 | --waline-bgcolor var(--background-color-1)
7 | --waline-bg-color var(--background-color-1)
8 | --waline-border-color var(--border-color)
9 | --waline-disable-bgcolor var(--background-color-2)
10 | --waline-disable-bg-color var(--background-color-2)
11 | --waline-disable-color var(--text-color-3)
12 | --waline-code-bgcolor rgba(40, 44, 52, 0.9)
13 | --waline-code-bg-color rgba(40, 44, 52, 0.9)
14 | --waline-info-bgcolor var(--background-color-3)
15 | --waline-info-bg-color var(--background-color-3)
16 | --waline-info-color var(--text-color-3)
17 | --waline-border 1px solid var(--waline-border-color)
18 | --waline-dark-grey var(--text-color-3)
19 | --waline-light-grey var(--text-color-4)
20 |
21 | input
22 | textarea {
23 | position relative
24 | box-sizing border-box
25 | padding 0.6rem 1rem
26 | color var(--text-color-3)
27 | background transparent
28 | border-radius 0.4rem
29 | outline none
30 |
31 | &:hover {
32 | background var(--background-color-2)
33 | }
34 | }
35 |
36 |
37 | .wl-header {
38 | box-sizing border-box
39 | padding 0
40 | }
41 |
42 |
43 | button {
44 | transition-t("color, background", "0, 0", "0.2, 0.2", "ease, ease")
45 | }
46 |
47 | .wl-logout-btn {
48 | svg {
49 | width 1rem
50 | height 1rem
51 | path {
52 | color var(--background-color-3)
53 | }
54 | }
55 | }
56 |
57 | .wl-panel {
58 | border-radius 0.4rem
59 | }
60 |
61 | .wl-count
62 | .wl-num
63 | .wl-preview h4
64 | .wl-like span {
65 | color var(--text-color-4)
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/common/empty-content.styl:
--------------------------------------------------------------------------------
1 | .empty-content-box {
2 | display flex
3 | justify-content center
4 | width 100%
5 | padding 4rem 0
6 |
7 | i.fa-solid {
8 | color var(--text-color-4)
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/first-screen.styl:
--------------------------------------------------------------------------------
1 | if (hexo-config('first_screen')) {
2 | $temp-fs-bg-img-light = hexo-config('first_screen.background_img')
3 | $temp-fs-bg-img-dark = hexo-config('first_screen.background_img_dark')
4 | }
5 | else {
6 | $temp-fs-bg-img-light = ''
7 | $temp-fs-bg-img-dark = ''
8 | }
9 |
10 | $fs-bg-img-light = $temp-fs-bg-img-light ? $temp-fs-bg-img-light : '/images/bg.svg'
11 | $fs-bg-img-dark = $temp-fs-bg-img-dark ? $temp-fs-bg-img-dark : $fs-bg-img-light
12 |
13 | $root = hexo-config('root')
14 |
15 | .first-screen-container {
16 | width 100%
17 | height 100vh
18 | overflow hidden
19 | background-image url($root + $fs-bg-img-light)
20 | background-position-x center
21 | background-position-y center
22 | background-size cover
23 | background-repeat-x no-repeat
24 | background-repeat-y no-repeat
25 |
26 |
27 | .dark-mode & {
28 | background-image url($root + $fs-bg-img-dark)
29 | }
30 |
31 | .first-screen-content {
32 | display flex
33 | flex-direction column
34 | align-items center
35 | justify-content space-between
36 | width 80%
37 | height 90%
38 | padding-top calc(var(--header-height) * 0.5)
39 |
40 | +keep-tablet() {
41 | padding-top calc(var(--header-height) * 0.5 * 0.9)
42 | }
43 |
44 | +keep-mobile() {
45 | padding-top calc(var(--header-height) * 0.5 * 0.8)
46 | }
47 |
48 | .description {
49 | display var(--first-screen-display)
50 | color var(--first-screen-font-color-light)
51 | font-weight bold
52 | font-size var(--first-screen-font-size)
53 | font-family var(--first-screen-font-family)
54 | line-height 1.8
55 | text-align center
56 | transform translateY(var(--first-screen-font-offset))
57 | opacity 0
58 |
59 | .dark-mode & {
60 | color var(--first-screen-font-color-dark)
61 |
62 | .desc-item {
63 | .desc
64 | .cursor {
65 | color var(--first-screen-font-color-dark)
66 | }
67 | }
68 | }
69 |
70 |
71 | .desc-item {
72 | .desc {
73 | color var(--first-screen-font-color-light)
74 | }
75 |
76 | .cursor {
77 | color var(--first-screen-font-color-light)
78 | animation blink-caret 0.8s step-end infinite
79 | }
80 | }
81 |
82 |
83 | +keep-tablet() {
84 | font-size calc(var(--first-screen-font-size) * 0.9)
85 | }
86 | }
87 |
88 |
89 | .bottom-placeholder {
90 | width 100%
91 |
92 | .sc-icon-list {
93 | display flex
94 | justify-content center
95 |
96 | .sc-icon-item {
97 | display flex
98 | align-items center
99 | margin 0 1rem
100 | cursor pointer
101 |
102 | i {
103 | color var(--first-screen-icon-color-light)
104 | font-size var(--first-screen-icon-size)
105 | }
106 |
107 |
108 | svg {
109 | width var(--first-screen-icon-size)
110 | height var(--first-screen-icon-size)
111 |
112 | path {
113 | fill var(--first-screen-icon-color-light)
114 | }
115 | }
116 |
117 |
118 | .dark-mode & {
119 | i {
120 | color var(--first-screen-icon-color-dark)
121 | }
122 |
123 | svg {
124 | path {
125 | fill var(--first-screen-icon-color-dark)
126 | }
127 | }
128 | }
129 |
130 |
131 | +keep-tablet() {
132 | i {
133 | font-size calc(var(--first-screen-icon-size) * 0.9)
134 | }
135 |
136 | svg {
137 | width calc(var(--first-screen-icon-size) * 0.9)
138 | height calc(var(--first-screen-icon-size) * 0.9)
139 | }
140 | }
141 |
142 |
143 | a {
144 | display flex !important
145 | align-items center !important
146 | }
147 | }
148 | }
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/footer.styl:
--------------------------------------------------------------------------------
1 | .footer {
2 | display flex
3 | flex-direction column
4 | align-items center
5 | justify-content center
6 | padding 1rem 0
7 | color var(--text-color-4)
8 | font-size 1rem
9 |
10 | a {
11 | color var(--text-color-4)
12 |
13 | &:hover {
14 | color var(--primary-color)
15 | }
16 |
17 | &.no-pointer {
18 | pointer-events none
19 | }
20 | }
21 |
22 |
23 | .info-item {
24 | position relative
25 | display flex
26 | flex-wrap wrap
27 | align-items center
28 | justify-content center
29 | box-sizing border-box
30 | margin 0.2rem 0
31 | color var(--text-color-4)
32 |
33 | &.count-info {
34 | display flex
35 |
36 | .count-item {
37 | &.uv
38 | &.pv {
39 | display none
40 | }
41 | }
42 | }
43 |
44 |
45 | &.deploy-info {
46 |
47 | display flex
48 |
49 | a
50 | .tooltip {
51 | display flex
52 | align-items center
53 | }
54 |
55 | img {
56 | height 1.08rem
57 | margin 0 0.4rem
58 | }
59 | }
60 |
61 |
62 | &.count-info
63 | &.record-info {
64 |
65 | .count-item
66 | .record-item {
67 | margin-right 15px
68 |
69 | &::before {
70 | position absolute
71 | top 50%
72 | left -10px
73 | box-sizing border-box
74 | transform translateY(-56%)
75 | content '|'
76 | }
77 |
78 |
79 | &:first-child {
80 | &::before {
81 | display none
82 | }
83 | }
84 |
85 |
86 | &:last-child {
87 | margin-right 0
88 | }
89 |
90 |
91 | .item-type {
92 | margin-right 4px
93 | }
94 | }
95 | }
96 | }
97 |
98 |
99 | .icon-animate {
100 | animation heartbeat-animate 1.2s ease-in-out infinite
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/image-mask.styl:
--------------------------------------------------------------------------------
1 | .zoom-in-image-mask {
2 | position fixed
3 | top 0
4 | right 0
5 | bottom 0
6 | left 0
7 | z-index $z-index-8
8 | display flex
9 | align-items center
10 | justify-content center
11 | box-sizing border-box
12 | background rgba(0, 0, 0, 0)
13 | visibility hidden
14 | transition-t("visibility, background", "0, 0", "0.3, 0.3", "linear, linear")
15 |
16 | &.show {
17 | background rgba(0, 0, 0, 0.5)
18 | visibility visible
19 |
20 | .zoom-in-image {
21 | cursor zoom-out
22 | }
23 | }
24 |
25 | .zoom-in-image {
26 | position absolute
27 | z-index $z-index-9
28 | transform-origin center center
29 | will-change transform
30 | transition-t("transform", "0", "0.3", "linear")
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/local-search.styl:
--------------------------------------------------------------------------------
1 | $icon-size = 1.2rem
2 | $search-header-height = 3rem
3 |
4 | .search-pop-overlay {
5 | position fixed
6 | top 0
7 | left 0
8 | z-index $z-index-8
9 | display flex
10 | width 100%
11 | height 100%
12 | background rgba(0, 0, 0, 0)
13 | visibility hidden
14 | transition-t("visibility, background", "0, 0", "0.3, 0.3", "ease, ease")
15 |
16 | &.active {
17 | background rgba(0, 0, 0, 0.35)
18 | visibility visible
19 |
20 | .search-popup {
21 | transform scale(1)
22 | }
23 | }
24 |
25 |
26 | .search-popup {
27 | z-index $z-index-6
28 | width 70%
29 | height 80%
30 | margin auto
31 | background var(--background-color-1)
32 | border-radius 0.4rem
33 | transform scale(0)
34 | transition-t("transform", "0", "0.3", "ease")
35 |
36 | +keep-tablet() {
37 | width 80%
38 | }
39 |
40 | +keep-mobile() {
41 | width 90%
42 | }
43 |
44 | .search-header {
45 | display flex
46 | align-items center
47 | height $search-header-height
48 | padding 0 1rem
49 | background var(--text-color-6)
50 | border-top-left-radius 0.2rem
51 | border-top-right-radius 0.2rem
52 |
53 | .search-input-field-pre {
54 | margin-right 0.2rem
55 | color var(--text-color-3)
56 | font-size 1.3rem
57 | cursor pointer
58 | }
59 |
60 | .search-input-container {
61 | flex-grow 1
62 | padding 0.2rem
63 |
64 | .search-input {
65 | width 100%
66 | color var(--text-color-3)
67 | font-size 1.2rem
68 | background transparent
69 | border 0
70 | outline 0
71 |
72 | &::-webkit-search-cancel-button {
73 | display none
74 | }
75 |
76 | &::-webkit-input-placeholder {
77 | color var(--text-color-4)
78 | font-size 1rem
79 | }
80 | }
81 | }
82 |
83 |
84 | .close-popup-btn {
85 | color var(--text-color-3)
86 | font-size $icon-size
87 | cursor pointer
88 |
89 | &:hover {
90 | color var(--text-color-1)
91 | }
92 | }
93 | }
94 |
95 |
96 | #search-result {
97 | position relative
98 | display flex
99 | box-sizing border-box
100 | height 'calc(100% - %s)' % $search-header-height
101 | padding 0.3rem 1.5rem
102 | overflow auto
103 |
104 | .search-result-list {
105 | width 100%
106 | height 100%
107 | font-size 1rem
108 |
109 | li {
110 | box-sizing border-box
111 | margin 0.8rem 0
112 | padding 0.8rem 0
113 | border-bottom 0.1rem dashed var(--border-color)
114 |
115 | &:last-child {
116 | border-bottom none
117 | }
118 |
119 |
120 | .search-result-title {
121 | position relative
122 | display flex
123 | align-items center
124 | margin-bottom 0.8rem
125 | padding-left 1rem
126 | font-weight bold
127 |
128 | &::after {
129 | position absolute
130 | top 50%
131 | left 0
132 | width 0.4rem
133 | height 0.4rem
134 | background var(--text-color-3)
135 | border-radius 50%
136 | transform translateY(-50%)
137 | content ''
138 | }
139 | }
140 |
141 |
142 | .search-result {
143 | margin 0
144 | padding-left 1rem
145 | line-height 2rem
146 | word-wrap break-word
147 | }
148 |
149 | a {
150 | &:hover {
151 | color var(--text-color-3)
152 | }
153 | }
154 |
155 | .search-keyword {
156 | color var(--primary-color)
157 | font-weight bold
158 | border-bottom 0.1rem dashed var(--primary-color)
159 | }
160 | }
161 | }
162 |
163 |
164 | #no-result {
165 | margin auto
166 | color var(--text-color-4)
167 | }
168 | }
169 | }
170 | }
171 |
172 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/paginator.styl:
--------------------------------------------------------------------------------
1 | .paginator {
2 | gap 1rem
3 | margin-top $page-container-gap
4 | user-select none
5 |
6 | .paginator-btn {
7 | cursor not-allowed
8 |
9 | a {
10 | padding 0.3rem 0.6rem
11 | pointer-events none
12 |
13 | i {
14 | color var(--text-color-4)
15 | font-size 1rem
16 | }
17 | }
18 |
19 |
20 | &.allowed {
21 | cursor pointer
22 |
23 | a {
24 | pointer-events auto
25 |
26 | i {
27 | color var(--text-color-3)
28 | }
29 | }
30 |
31 | &:hover {
32 | a {
33 | i {
34 | color var(--primary-color)
35 | }
36 | }
37 | }
38 | }
39 | }
40 |
41 |
42 | .base-style {
43 | color var(--text-color-4)
44 | font-size 1.1rem
45 | }
46 |
47 |
48 | .page-number-box {
49 | margin 0 0.2rem
50 |
51 | .page-number-input {
52 | width 2.8rem
53 | height 2rem
54 | margin 0
55 | padding 0
56 | font-weight 400 !important
57 | font-size 1rem
58 | text-align center
59 | background none
60 | border 0.1rem solid var(--border-color)
61 | border-radius 0.3rem
62 | outline none
63 | -webkit-appearance none
64 | transition-t("width, border-color", "0, 0", "0.2, 0.2", "ease, ease")
65 |
66 |
67 | &::-webkit-outer-spin-button
68 | &::-webkit-inner-spin-button {
69 | -webkit-appearance none !important
70 | }
71 |
72 | &:hover
73 | &:focus {
74 | width 3.8rem
75 | border-color var(--primary-color)
76 | }
77 | }
78 |
79 |
80 | .delimiter {
81 | margin 0 0.6rem
82 | }
83 | }
84 | }
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/post-meta-info.styl:
--------------------------------------------------------------------------------
1 | $home-page-font-size = 0.92rem
2 | $post-page-font-size = 0.86rem
3 | $meta-icon-interspace = 0.4rem
4 | $meta-item-interspace = 0.7rem
5 |
6 |
7 | .post-meta-info-container {
8 | color var(--text-color-4)
9 |
10 | &.post {
11 | font-size $post-page-font-size
12 |
13 | i.icon
14 | i.fas {
15 | font-size $post-page-font-size
16 | }
17 | }
18 |
19 |
20 | &.home {
21 | display flex
22 | align-items center
23 | justify-content space-between
24 | font-size $home-page-font-size
25 |
26 | i.icon
27 | i.fas {
28 | font-size $home-page-font-size
29 | }
30 | }
31 |
32 |
33 | a {
34 | color var(--text-color-4)
35 |
36 | &:hover {
37 | color var(--primary-color)
38 | }
39 | }
40 |
41 |
42 |
43 | .post-meta-info {
44 | display flex
45 | flex-wrap wrap
46 | justify-content flex-start
47 | letter-spacing 0.6px
48 |
49 | .meta-info-item {
50 | display flex
51 | align-items center
52 | justify-content flex-start
53 | box-sizing border-box
54 | margin-right $meta-item-interspace
55 |
56 | &:last-child {
57 | margin-right 0
58 | }
59 |
60 | ul
61 | li {
62 | display inline
63 | }
64 | }
65 |
66 |
67 | .post-create-date {
68 | display var(--post-create-datetime)
69 | }
70 |
71 |
72 | .post-update-date {
73 | display var(--post-update-datetime)
74 | }
75 |
76 |
77 | .post-category {
78 | word-spacing 0
79 |
80 | .category-item {
81 |
82 | i.icon {
83 | font-size $home-page-font-size * 0.7
84 | }
85 | }
86 |
87 |
88 | +keep-mobile() {
89 | display none !important
90 | }
91 | }
92 |
93 |
94 | .post-tag {
95 | word-spacing 0
96 |
97 | .tag-item {
98 | margin-right $meta-icon-interspace * 0.8
99 |
100 | &:last-child {
101 | margin-right 0
102 | }
103 |
104 | .tag-separator {
105 | margin-right $meta-icon-interspace * 0.5
106 |
107 | i.icon {
108 | font-size $home-page-font-size * 0.92
109 | }
110 | }
111 | }
112 |
113 |
114 | +keep-tablet() {
115 | display none !important
116 | }
117 | }
118 |
119 |
120 | .post-pv {
121 | display none
122 | }
123 | }
124 |
125 |
126 | .home-read-more {
127 | display flex
128 | align-items center
129 | justify-content space-between
130 | font-size $home-page-font-size
131 |
132 | i.fas {
133 | color var(--text-color-4)
134 | font-size $home-page-font-size * 1.1
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/post/post-copyright-info.styl:
--------------------------------------------------------------------------------
1 | .post-copyright-info-container {
2 | width 100%
3 | margin-top 1rem
4 | background var(--background-color-2)
5 | border-radius calc(var(--box-border-radius) * 0.6)
6 |
7 | .copyright-info-content {
8 | width 100%
9 | padding 1.3rem
10 | overflow hidden
11 | font-size 1rem
12 |
13 |
14 | .copyright-info-top {
15 | width 100%
16 |
17 | .copyright-post-title {
18 | width 100%
19 | color var(--text-color-3)
20 | font-size 1.1rem
21 | }
22 |
23 | .copyright-post-link {
24 | width 100%
25 | margin-top 0.2rem
26 | color var(--text-color-4)
27 | }
28 | }
29 |
30 |
31 | .copyright-info-bottom {
32 | display flex
33 | justify-content flex-start
34 | margin-top 1rem
35 |
36 | .bottom-item {
37 | z-index $z-index-10
38 | margin-right 1.2rem
39 |
40 | .type {
41 | color var(--text-color-4)
42 | }
43 |
44 | .content {
45 | margin-top 0.2rem
46 | color var(--text-color-3)
47 | }
48 | }
49 |
50 |
51 | .post-license {
52 | .content {
53 | i {
54 | font-size 1.1rem
55 | }
56 | }
57 | }
58 | }
59 |
60 | .copyright-bg {
61 | position absolute
62 | top 50%
63 | right 2rem
64 | color var(--copyright-icon-bg-color)
65 | font-size 14rem
66 | transform translateY(-50%)
67 |
68 | +keep-mobile() {
69 | font-size 12rem
70 | }
71 | }
72 | }
73 |
74 |
75 | .copy-copyright-info {
76 | position absolute
77 | top 0.5rem
78 | right 0.5rem
79 | box-sizing border-box
80 | padding 0.3rem
81 | cursor pointer
82 |
83 | i {
84 | color var(--text-color-3)
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/post/post-share.styl:
--------------------------------------------------------------------------------
1 | $share-item-width = 1.8rem
2 |
3 | .post-share-container {
4 | flex-shrink 0
5 |
6 | .share-list-wrap {
7 | display flex
8 | justify-content flex-end
9 |
10 | .share-item {
11 | width $share-item-width
12 | height $share-item-width
13 | margin-left 0.5rem
14 | padding 0.4rem
15 | border-style solid
16 | border-width 0.1rem
17 | border-radius 50%
18 | cursor pointer
19 | transition-t("background", "0", "0.3", "ease")
20 |
21 |
22 | i {
23 | color inherit
24 | font-size 1rem
25 | }
26 |
27 |
28 | &.qq {
29 | color var(--keep-primary-color)
30 | border-color var(--keep-primary-color)
31 |
32 | &:hover {
33 | color var(--background-color-1)
34 | background var(--keep-primary-color)
35 | }
36 | }
37 |
38 |
39 | &.wechat {
40 | color var(--keep-success-color)
41 | border-color var(--keep-success-color)
42 |
43 | img {
44 | filter brightness(1) !important
45 |
46 | &[lazyload] {
47 | &::before {
48 | background #fff !important
49 | }
50 | }
51 | }
52 |
53 | &:hover {
54 | color var(--background-color-1)
55 | background var(--keep-success-color)
56 | }
57 | }
58 |
59 |
60 | &.weibo {
61 | color var(--keep-danger-color)
62 | border-color var(--keep-danger-color)
63 |
64 | &:hover {
65 | color var(--background-color-1)
66 | background var(--keep-danger-color)
67 | }
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/post/post-tools.styl:
--------------------------------------------------------------------------------
1 | $li-margin-bottom = 0.8rem
2 | $post-tool-button-width = 2.5rem
3 |
4 | .post-tools-container {
5 | padding-top var(--component-gap)
6 |
7 | .post-tools-list {
8 | li {
9 | margin-bottom $li-margin-bottom
10 |
11 | &:last-child {
12 | margin-bottom 0
13 | }
14 | }
15 |
16 |
17 | li.tools-item {
18 | position relative
19 | box-sizing border-box
20 | width $post-tool-button-width
21 | height $post-tool-button-width
22 | color var(--text-color-3)
23 | font-size 1.2rem
24 | background var(--background-color-1)
25 | border-radius 50%
26 | box-shadow 2px 2px 5px var(--shadow-color)
27 | cursor pointer
28 |
29 | &:hover {
30 | box-shadow 2px 2px 8px var(--shadow-hover-color)
31 | }
32 |
33 | i {
34 | color var(--text-color-3)
35 | }
36 |
37 |
38 | &:hover {
39 | color var(--background-color-1)
40 | background var(--primary-color)
41 |
42 | i {
43 | color var(--background-color-1) !important
44 | }
45 | }
46 |
47 |
48 | &.toggle-show-toc {
49 | display none
50 | }
51 |
52 |
53 | &.go-to-comments {
54 | .post-comments-count {
55 | position absolute
56 | top 0
57 | right -1rem
58 | display none
59 | align-items center
60 | justify-content center
61 | box-sizing border-box
62 | min-width 1.1rem
63 | height 1.1rem
64 | padding 0 0.2rem
65 | color var(--badge-color)
66 | font-size 12px
67 | background var(--badge-background-color)
68 | border-radius 0.4rem
69 |
70 | +keep-tablet() {
71 | display none !important
72 | }
73 | }
74 | }
75 | }
76 |
77 |
78 | li.status-item {
79 | width $post-tool-button-width
80 | height $post-tool-button-width
81 | color var(--text-color-3)
82 | font-size 1.6rem
83 | cursor pointer
84 |
85 |
86 | &.post-lock {
87 | cursor default
88 |
89 | .fa-lock-open {
90 | display none
91 | color var(--keep-success-color)
92 | }
93 |
94 | &.decrypt {
95 | cursor pointer
96 |
97 | .fa-lock-open {
98 | display block
99 | }
100 |
101 | .fa-lock {
102 | display none
103 | }
104 | }
105 | }
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/post/reward-author.styl:
--------------------------------------------------------------------------------
1 | .reward-author-container {
2 | margin-top 1.6rem
3 |
4 | .tooltip-img {
5 | .tooltip-img-box {
6 | img {
7 | max-height 13rem
8 | }
9 | }
10 | }
11 |
12 |
13 | .reward-btn {
14 | padding 0.6rem 1rem
15 | color var(--text-color-3)
16 | font-size 1rem
17 | background -webkit-linear-gradient(0deg, var(--keep-primary-color) 15%, var(--keep-danger-color), var(--keep-warning-color), var(--keep-success-color), var(--keep-primary-color))
18 | -webkit-background-clip text
19 | background-clip text
20 | border-radius 0.8rem
21 | box-shadow 0 0 0.4rem var(--shadow-color)
22 | cursor pointer
23 |
24 | &:hover {
25 | font-weight bold
26 | -webkit-text-fill-color transparent
27 | }
28 |
29 |
30 | i {
31 | color var(--text-color-3)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/progress-bar.styl:
--------------------------------------------------------------------------------
1 | .progress-bar-container {
2 | position fixed
3 | top 0
4 | left 0
5 | z-index $z-index-9
6 | width 100%
7 |
8 | if (hexo-config('pjax') && hexo-config('pjax.enable') == true) {
9 |
10 | .pjax-progress-bar {
11 | position absolute
12 | top 0
13 | left 0
14 | z-index $z-index-8
15 | width 0
16 | height 2px
17 | background var(--pjax-progress-bar-color)
18 | visibility hidden
19 | opacity 0
20 | transition-t("width, opacity", "0, 0", "0.1, 0.1", "linear")
21 |
22 |
23 | &.show {
24 | visibility visible
25 | opacity 1
26 | }
27 | }
28 |
29 |
30 | .pjax-progress-icon {
31 | position absolute
32 | top 0.4rem
33 | right 0.3rem
34 | z-index $z-index-8
35 | color var(--text-color-3)
36 | font-size 1.1rem
37 | visibility hidden
38 |
39 | +keep-tablet() {
40 | top 0.3rem
41 | right 0.2rem
42 | font-size 1rem
43 | }
44 |
45 | &.show {
46 | visibility visible
47 | }
48 | }
49 | }
50 |
51 | if (hexo-config('scroll') && hexo-config('scroll.progress_bar') == true) {
52 | .scroll-progress-bar {
53 | position absolute
54 | top 0
55 | left 0
56 | z-index $z-index-7
57 | width 0
58 | height var(--header-scroll-progress-bar-height)
59 | background var(--primary-color)
60 | visibility hidden
61 | transition-t("width", "0", "0.1", "linear")
62 |
63 | &.hide {
64 | display none !important
65 | }
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/side-tools.styl:
--------------------------------------------------------------------------------
1 | $tools-item-width = 2.2rem
2 | $tools-item-font-size = 1.1rem
3 | $tools-item-border-radius = 0.2rem
4 |
5 | .side-tools-show-handle {
6 | transform translateX(100%)
7 | opacity 0
8 | transition-t("transform, opacity", "0, 0", "0.26, 0.26", "linear, linear")
9 |
10 | &.show {
11 | transform translateX(0)
12 | opacity 1
13 | }
14 | }
15 |
16 | .side-tools-container {
17 | .tools-item {
18 | width $tools-item-width
19 | height $tools-item-width
20 | margin-bottom 0.25rem
21 | color var(--text-color-3)
22 | font-size $tools-item-font-size
23 | background var(--background-color-1)
24 | border-right none
25 | border-radius $tools-item-border-radius
26 | box-shadow 0.1rem 0.1rem 0.2rem var(--shadow-color)
27 | cursor pointer
28 |
29 | i {
30 | color var(--text-color-3)
31 | }
32 |
33 | &:hover {
34 | color var(--background-color-1)
35 | background var(--primary-color)
36 | box-shadow 0.2rem 0.2rem 0.4rem var(--shadow-color)
37 |
38 | i {
39 | color var(--background-color-1)
40 | }
41 | }
42 |
43 |
44 | +keep-tablet() {
45 | width $tools-item-width * 0.9
46 | height $tools-item-width * 0.9
47 | font-size $tools-item-font-size * 0.9
48 |
49 | &.toggle-show-toc-tablet
50 | &.go-to-comments-tablet {
51 | display flex !important
52 | }
53 | }
54 |
55 | &.rss {
56 |
57 | a {
58 | width 100%
59 | height 100%
60 | border-radius $tools-item-border-radius
61 |
62 | &:hover {
63 | color var(--background-color-1)
64 | background var(--primary-color)
65 | box-shadow 0.2rem 0.2rem 0.4rem var(--shadow-color)
66 | }
67 | }
68 | }
69 |
70 | &.toggle-show-toc-tablet
71 | &.go-to-comments-tablet {
72 | display none
73 | }
74 | }
75 |
76 |
77 | .exposed-tools-list {
78 |
79 | .tool-scroll-to-top {
80 | display none
81 |
82 | &.show {
83 | display flex
84 | }
85 |
86 |
87 | &.show-arrow {
88 | .percent {
89 | display none
90 | }
91 |
92 | .arrow {
93 | display flex
94 | }
95 | }
96 |
97 |
98 | &.show-percent {
99 | .percent {
100 | display flex
101 | }
102 |
103 | .arrow {
104 | display none
105 | }
106 | }
107 |
108 |
109 | &:hover {
110 | .percent {
111 | display none
112 | }
113 |
114 | .arrow {
115 | display flex
116 | }
117 | }
118 |
119 |
120 | .percent {
121 | font-size 1rem
122 | }
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/source/css/layout/_partial/toc.styl:
--------------------------------------------------------------------------------
1 | .post-toc-wrap {
2 | width 100%
3 | height 100%
4 | overflow-y auto
5 | font-size 0.92rem
6 |
7 | &::-webkit-scrollbar-thumb {
8 | background var(--toc-scrollbar-color)
9 | border-radius 0.3rem
10 | }
11 |
12 | &::-webkit-scrollbar-track {
13 | background transparent
14 | }
15 |
16 | .post-toc {
17 |
18 | ol {
19 | position relative
20 | box-sizing border-box
21 | margin 0
22 | text-align left
23 | list-style none
24 |
25 | a {
26 | transition-property all
27 | }
28 | }
29 |
30 |
31 | .nav {
32 |
33 | .nav-item {
34 | position relative
35 | box-sizing border-box
36 | overflow hidden
37 | line-height 2
38 | white-space nowrap
39 | text-overflow ellipsis
40 |
41 |
42 | &.active {
43 | > .nav-child {
44 | display block
45 | }
46 | }
47 |
48 |
49 | .nav-link {
50 | position relative
51 | box-sizing border-box
52 | padding-left 0.8rem
53 |
54 | &:hover {
55 | .nav-number
56 | .nav-text {
57 | color var(--primary-color)
58 | }
59 | }
60 |
61 | &.active-current {
62 | > .nav-child {
63 | display block
64 |
65 | > .nav-item {
66 | display block
67 | }
68 | }
69 |
70 |
71 | &::before {
72 | position absolute
73 | top 50%
74 | left 0
75 | width 0.2rem
76 | height 1.2rem
77 | background var(--primary-color)
78 | border-radius 0.2rem
79 | transform translateY(-50%)
80 | content ''
81 | }
82 |
83 | .nav-number
84 | .nav-text {
85 | color var(--primary-color)
86 | }
87 | }
88 |
89 |
90 | .nav-number
91 | .nav-text {
92 | color var(--toc-text-color)
93 | }
94 | }
95 |
96 |
97 | .nav-child {
98 | padding-left 1rem
99 |
100 |
101 | if (hexo-config('toc') && hexo-config('toc.expand_all') == true) {
102 | display block
103 | }
104 | else {
105 | display none
106 | }
107 | }
108 | }
109 | }
110 | }
111 | }
112 |
113 |
--------------------------------------------------------------------------------
/source/css/layout/_template/page-template.styl:
--------------------------------------------------------------------------------
1 | $page-container-gap = 2rem
2 |
3 | .page-template-container {
4 |
5 | &.keep-container {
6 | keep-container(1, 0, $page-container-gap)
7 |
8 | .page-template-bottom {
9 | padding $page-container-gap
10 | }
11 | }
12 |
13 | &.has-page-cover {
14 | .page-template-bottom {
15 | padding-top $page-container-gap
16 | padding-bottom $page-container-gap
17 | }
18 | }
19 |
20 |
21 | .page-template-top {
22 | width 100%
23 | overflow hidden
24 | background-color var(--background-color-2)
25 | border-top-left-radius var(--box-border-radius)
26 | border-top-right-radius var(--box-border-radius)
27 |
28 | .page-cover {
29 | position absolute
30 | top 0
31 | left 0
32 | box-sizing border-box
33 | width 100%
34 | height 100%
35 | object-fit cover
36 | }
37 |
38 | .page-cover-title {
39 | position absolute
40 | bottom 1rem
41 | left 1rem
42 | box-sizing border-box
43 | padding 1rem 1.6rem
44 | overflow-y auto
45 | color var(--text-color-3)
46 | font-weight 600
47 | font-size 1.6rem
48 | line-height 1.6
49 | background var(--background-color-1-transparent)
50 | border-radius var(--box-border-radius)
51 | -webkit-backdrop-filter blur(1px)
52 | backdrop-filter blur(1px)
53 | }
54 | }
55 |
56 |
57 | .page-template-bottom {
58 | width 100%
59 |
60 | .page-content {
61 | color var(--text-color-3)
62 |
63 | &.has-data {
64 | margin-top $page-container-gap
65 | }
66 |
67 | h1
68 | h2
69 | h3
70 | h4
71 | h5
72 | h6 {
73 |
74 | &:first-child {
75 | margin-top 0
76 | }
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/source/css/layout/_template/photo-album.styl:
--------------------------------------------------------------------------------
1 | $album-img-gap = 0.8rem
2 |
3 | if (hexo-config('source_data.photos')) {
4 | .page-template-container {
5 | .photo-album-box {
6 | column-count 3
7 | column-gap $album-img-gap
8 |
9 | img.photo {
10 | display block
11 | width 100%
12 | margin-bottom $album-img-gap
13 | background var(--background-color-2)
14 | border-radius 0.6rem
15 | cursor zoom-in
16 | }
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/source/css/style.styl:
--------------------------------------------------------------------------------
1 | @import "common/basic.styl"
2 | @import "common/markdown.styl"
3 | @import "common/code-block/highlight.styl"
4 | @import "common/code-block/code-block.styl"
5 | @import "common/code-block/code-theme.styl"
6 | @import "common/tags/keep-note.styl"
7 | @import "common/tags/keep-button.styl"
8 | @import "common/tags/keep-tabs.styl"
9 | @import "layout/page.styl"
10 | @import "layout/_partial/local-search.styl"
11 | @import "layout/_partial/toc.styl"
12 | @import "layout/_partial/comment/comment.styl"
13 | @import "layout/_partial/progress-bar.styl"
14 | @import "layout/_partial/header.styl"
15 | @import "layout/_partial/post/post-tools.styl"
16 | @import "layout/_partial/side-tools.styl"
17 | @import "layout/_partial/archive-list.styl"
18 | @import "layout/_partial/footer.styl"
19 | @import "layout/_partial/paginator.styl"
20 | @import "layout/_partial/first-screen.styl"
21 | @import "layout/_partial/image-mask.styl"
22 | @import "layout/_partial/post-meta-info.styl"
23 | @import "layout/_partial/post/post-copyright-info.styl"
24 | @import "layout/_page/home.styl"
25 | @import "layout/_page/archive.styl"
26 | @import "layout/_page/post.styl"
27 | @import "layout/_page/category.styl"
28 | @import "layout/_page/category-archive.styl"
29 | @import "layout/_page/tag.styl"
30 | @import "layout/_page/tag-archive.styl"
31 | @import "layout/_page/404.styl"
32 | @import "layout/_template/page-template.styl"
33 | @import "layout/_template/friends-link.styl"
34 | @import "layout/_template/photo-album.styl"
35 | @import "layout/_template/tools-nav.styl"
36 | @import "layout/_partial/common/empty-content.styl"
37 | @import "layout/_partial/post/post-share.styl"
38 | @import "layout/_partial/post/reward-author.styl"
39 |
--------------------------------------------------------------------------------
/source/font/css/regular.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com
3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4 | * Copyright 2023 Fonticons, Inc.
5 | */
6 | :host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}.fa-regular,.far{font-weight:400}
--------------------------------------------------------------------------------
/source/font/css/solid.min.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com
3 | * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4 | * Copyright 2023 Fonticons, Inc.
5 | */
6 | :host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}
--------------------------------------------------------------------------------
/source/font/webfonts/fa-brands-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/font/webfonts/fa-brands-400.ttf
--------------------------------------------------------------------------------
/source/font/webfonts/fa-brands-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/font/webfonts/fa-brands-400.woff2
--------------------------------------------------------------------------------
/source/font/webfonts/fa-regular-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/font/webfonts/fa-regular-400.ttf
--------------------------------------------------------------------------------
/source/font/webfonts/fa-regular-400.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/font/webfonts/fa-regular-400.woff2
--------------------------------------------------------------------------------
/source/font/webfonts/fa-solid-900.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/font/webfonts/fa-solid-900.ttf
--------------------------------------------------------------------------------
/source/font/webfonts/fa-solid-900.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/font/webfonts/fa-solid-900.woff2
--------------------------------------------------------------------------------
/source/font/webfonts/fa-v4compatibility.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/font/webfonts/fa-v4compatibility.ttf
--------------------------------------------------------------------------------
/source/font/webfonts/fa-v4compatibility.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/font/webfonts/fa-v4compatibility.woff2
--------------------------------------------------------------------------------
/source/images/avatar.svg:
--------------------------------------------------------------------------------
1 |
43 |
--------------------------------------------------------------------------------
/source/images/bg.svg:
--------------------------------------------------------------------------------
1 |
38 |
--------------------------------------------------------------------------------
/source/images/bg2.svg:
--------------------------------------------------------------------------------
1 |
38 |
--------------------------------------------------------------------------------
/source/images/brands/aliyun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/images/brands/aliyun.png
--------------------------------------------------------------------------------
/source/images/brands/cloudflare.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/images/brands/cloudflare.png
--------------------------------------------------------------------------------
/source/images/brands/gitee.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/images/brands/gitee.png
--------------------------------------------------------------------------------
/source/images/brands/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/images/brands/github.png
--------------------------------------------------------------------------------
/source/images/brands/netlify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/images/brands/netlify.png
--------------------------------------------------------------------------------
/source/images/brands/tencent_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/images/brands/tencent_cloud.png
--------------------------------------------------------------------------------
/source/images/brands/upyun.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/images/brands/upyun.png
--------------------------------------------------------------------------------
/source/images/brands/vercel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XPoet/hexo-theme-keep/032f1bcfababaa9839cdf3b568d01e6bdb6bfbdd/source/images/brands/vercel.png
--------------------------------------------------------------------------------
/source/images/logo.svg:
--------------------------------------------------------------------------------
1 |
42 |
--------------------------------------------------------------------------------
/source/js/back2top.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | KEEP.initBack2Top = () => {
4 | KEEP.utils.back2Top = {
5 | back2BottomBtn: document.querySelector('.tool-scroll-to-bottom'),
6 | back2TopBtn: document.querySelector('.tool-scroll-to-top'),
7 |
8 | back2top() {
9 | window.anime({
10 | targets: document.scrollingElement,
11 | easing: 'easeOutExpo',
12 | scrollTop: 0
13 | })
14 | },
15 |
16 | back2Bottom() {
17 | const scrollHeight = document.body.scrollHeight || document.documentElement.scrollHeight
18 | window.anime({
19 | targets: document.scrollingElement,
20 | easing: 'easeInExpo',
21 | scrollTop: scrollHeight
22 | })
23 | },
24 |
25 | initBack2Top() {
26 | this.back2TopBtn &&
27 | this.back2TopBtn.addEventListener('click', () => {
28 | this.back2top()
29 | })
30 | },
31 |
32 | initBack2Bottom() {
33 | this.back2BottomBtn &&
34 | this.back2BottomBtn.addEventListener('click', () => {
35 | this.back2Bottom()
36 | })
37 | }
38 | }
39 |
40 | KEEP.utils.back2Top.initBack2Top()
41 | KEEP.utils.back2Top.initBack2Bottom()
42 | }
43 |
--------------------------------------------------------------------------------
/source/js/header-shrink.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | KEEP.initHeaderShrink = () => {
4 | KEEP.utils.headerShrink = {
5 | headerWrapperDom: null,
6 | isHeaderShrink: false,
7 | headerHeight: 70,
8 | drawerMenuListDom: document.querySelector('.header-drawer .drawer-menu-list'),
9 | windowMaskDom: document.querySelector('.window-mask'),
10 |
11 | init() {
12 | this.headerWrapperDom = document.querySelector('.header-wrapper')
13 | if (this.headerWrapperDom) {
14 | this.headerHeight = this.headerWrapperDom.getBoundingClientRect().height
15 | }
16 | },
17 |
18 | headerShrink() {
19 | const fullPageHeight = KEEP.utils.getFullPageHeight()
20 | if (fullPageHeight < window.innerHeight + 2 * this.headerHeight) {
21 | return
22 | }
23 |
24 | const scrollTop = KEEP.utils.getScrollTop()
25 | const isHeaderTransparent =
26 | KEEP.theme_config?.first_screen?.enable === true &&
27 | !window.location.pathname.includes('/page/')
28 |
29 | if (!this.isHeaderShrink && scrollTop > this.headerHeight) {
30 | this.isHeaderShrink = true
31 | document.body.classList.add('header-shrink')
32 | if (isHeaderTransparent) {
33 | this.headerWrapperDom.classList.add('transparent-2')
34 | }
35 | } else if (this.isHeaderShrink && scrollTop <= this.headerHeight) {
36 | this.isHeaderShrink = false
37 | document.body.classList.remove('header-shrink')
38 | if (isHeaderTransparent) {
39 | this.headerWrapperDom.classList.remove('transparent-2')
40 | }
41 | }
42 | },
43 |
44 | cleanHeaderShrink() {
45 | document.body.classList.remove('header-shrink')
46 | this.isHeaderShrink = false
47 | },
48 |
49 | sideToolsBarShowHandle() {
50 | const scrollTop = KEEP.utils.getScrollTop()
51 | const sideToolsDom = document.querySelector('.side-tools .side-tools-container')
52 | if (scrollTop > this.headerHeight / 2) {
53 | sideToolsDom.classList.add('show')
54 | } else {
55 | sideToolsDom.classList.remove('show')
56 | }
57 | },
58 |
59 | toggleHeaderDrawerShow() {
60 | const domList = [this.windowMaskDom, document.querySelector('.menu-bar')]
61 |
62 | if (KEEP.theme_config?.pjax?.enable === true) {
63 | domList.push(...this.drawerMenuListDom.querySelectorAll('.not-sub-menu'))
64 | domList.push(...this.drawerMenuListDom.querySelectorAll('.has-sub-menu .sub-menu-item'))
65 | }
66 |
67 | domList.forEach((v) => {
68 | v.addEventListener('click', () => {
69 | document.body.classList.toggle('show-header-drawer')
70 | })
71 | })
72 | },
73 |
74 | // menu nav jump handle
75 | menuNavJumpHandle() {
76 | const menuLabels = this.drawerMenuListDom.querySelectorAll('.has-sub-menu .drawer-menu-label')
77 | menuLabels.forEach((menu) => {
78 | menu.addEventListener('click', () => {
79 | menu.parentElement.classList.toggle('show-sub-menu')
80 | })
81 | })
82 | },
83 |
84 | closeHeaderDrawer() {
85 | const siteInfoDom = document.querySelector('.header-wrapper .header-content .left')
86 | siteInfoDom.addEventListener('click', () => {
87 | document.body.classList.remove('show-header-drawer')
88 | })
89 | }
90 | }
91 | KEEP.utils.headerShrink.init()
92 | KEEP.utils.headerShrink.headerShrink()
93 | KEEP.utils.headerShrink.toggleHeaderDrawerShow()
94 | KEEP.utils.headerShrink.menuNavJumpHandle()
95 | KEEP.utils.headerShrink.closeHeaderDrawer()
96 | KEEP.utils.headerShrink.cleanHeaderShrink()
97 | }
98 |
--------------------------------------------------------------------------------
/source/js/lazyload.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | KEEP.initLazyLoad = () => {
4 | if (KEEP?.theme_config?.lazyload?.enable !== true) {
5 | return
6 | }
7 |
8 | const imgs = document.querySelectorAll('img')
9 | let now = Date.now()
10 | let needLoad = true
11 |
12 | function lazyload(imgs) {
13 | now = Date.now()
14 | needLoad = Array.from(imgs).some((i) => i.hasAttribute('lazyload'))
15 | const viewOffsetTop =
16 | window.innerHeight + document.documentElement.scrollTop || document.body.scrollTop
17 |
18 | imgs.forEach((img) => {
19 | if (img.hasAttribute('lazyload') && !img.hasAttribute('loading')) {
20 | const imgOffsetTop = window.scrollY + img.getBoundingClientRect().top
21 |
22 | if (viewOffsetTop > imgOffsetTop) {
23 | img.setAttribute('loading', true)
24 | const loadImageTimeout = setTimeout(() => {
25 | const tempImg = new Image()
26 | const src = img.getAttribute('data-src')
27 | tempImg.src = src
28 | tempImg.onload = () => {
29 | img.src = src
30 | img.removeAttribute('lazyload')
31 | img.removeAttribute('loading')
32 | clearTimeout(loadImageTimeout)
33 | }
34 | }, 500)
35 | }
36 | }
37 | })
38 | }
39 |
40 | lazyload(imgs)
41 |
42 | window.onscroll = () => {
43 | if (Date.now() - now > 50 && needLoad) {
44 | if (imgs.length) {
45 | lazyload(imgs)
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/source/js/main.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | window.addEventListener('DOMContentLoaded', () => {
4 | const { version, local_search, lazyload } = KEEP.theme_config
5 |
6 | KEEP.themeInfo = {
7 | theme: `Keep v${version}`,
8 | author: 'XPoet',
9 | repository: 'https://github.com/XPoet/hexo-theme-keep',
10 | localStorageKey: 'KEEP-THEME-STATUS',
11 | encryptKey: 'KEEP-ENCRYPT',
12 | styleStatus: {
13 | isDark: false,
14 | fontSizeLevel: 0,
15 | isShowToc: true
16 | },
17 | defaultDatetimeFormat: 'YYYY-MM-DD HH:mm:ss'
18 | }
19 |
20 | // print theme base info
21 | KEEP.printThemeInfo = () => {
22 | console.log(
23 | `\n %c ${KEEP.themeInfo.theme} %c ${KEEP.themeInfo.repository} \n`,
24 | `color: #fadfa3; background: #333; padding: 6px 0;`,
25 | `padding: 6px 0;`
26 | )
27 | }
28 | KEEP.printThemeInfo()
29 |
30 | // set version number of footer
31 | KEEP.setFooterVersion = () => {
32 | const vd = document.querySelector('.footer .keep-version')
33 | vd && (vd.innerHTML = KEEP.themeInfo.theme)
34 | }
35 |
36 | // set styleStatus to localStorage
37 | KEEP.setStyleStatus = () => {
38 | localStorage.setItem(KEEP.themeInfo.localStorageKey, JSON.stringify(KEEP.themeInfo.styleStatus))
39 | }
40 |
41 | // get styleStatus from localStorage
42 | KEEP.getStyleStatus = () => {
43 | let temp = localStorage.getItem(KEEP.themeInfo.localStorageKey)
44 | if (temp) {
45 | temp = JSON.parse(temp)
46 | for (let key in KEEP.themeInfo.styleStatus) {
47 | KEEP.themeInfo.styleStatus[key] = temp[key]
48 | }
49 | return temp
50 | } else {
51 | return null
52 | }
53 | }
54 |
55 | // init prototype function
56 | KEEP.initPrototype = () => {
57 | HTMLElement.prototype.wrap = function (wrapper) {
58 | this.parentNode.insertBefore(wrapper, this)
59 | this.parentNode.removeChild(this)
60 | wrapper.appendChild(this)
61 | }
62 | }
63 | KEEP.initPrototype()
64 |
65 | KEEP.initExecute = () => {
66 | KEEP.initUtils()
67 | KEEP.initHeaderShrink()
68 | KEEP.initModeToggle()
69 | KEEP.initBack2Top()
70 | KEEP.initCodeBlock()
71 | KEEP.setFooterVersion()
72 |
73 | if (lazyload?.enable === true) {
74 | KEEP.initLazyLoad()
75 | }
76 |
77 | if (local_search?.enable === true) {
78 | KEEP.initLocalSearch()
79 | }
80 | }
81 | KEEP.initExecute()
82 | })
83 |
--------------------------------------------------------------------------------
/source/js/page/category-page.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | function resetCategoriesPage() {
4 | const resetCategoryDom = (domList) => {
5 | domList.forEach((dom) => {
6 | const categoryNameDom = dom.querySelector('.site-all-category-list-link')
7 | const categoryCountDom = dom.querySelector('.site-all-category-list-count')
8 | const childCategoryInfo = dom.querySelector('.site-all-category-list-child')
9 |
10 | let domTemplate = `
11 |
12 |
${
13 | childCategoryInfo
14 | ? ' '
15 | : ''
16 | }${categoryNameDom.outerHTML}
17 |
${categoryCountDom.outerHTML}
18 |
19 | `
20 |
21 | if (childCategoryInfo) {
22 | resetCategoryDom(childCategoryInfo.querySelectorAll('.site-all-category-list-item'))
23 | domTemplate += childCategoryInfo.outerHTML
24 | }
25 |
26 | dom.innerHTML = domTemplate
27 | })
28 | }
29 |
30 | const expandHandle = () => {
31 | const selfCategoryInfoDom = document.querySelectorAll('.site-all-category-list-item')
32 | selfCategoryInfoDom.forEach((dom) => {
33 | let isExpand = false
34 | const iconDom = dom.querySelector('.self-category-info .left .icon')
35 | const childDom = dom.querySelector('.site-all-category-list-child')
36 |
37 | iconDom &&
38 | iconDom.addEventListener('click', () => {
39 | isExpand = !isExpand
40 | if (childDom) {
41 | if (isExpand) {
42 | childDom.style.height = 'auto'
43 | childDom.style.visibility = 'visible'
44 | iconDom.classList.add('fa-square-minus')
45 | iconDom.classList.remove('fa-square-plus')
46 | } else {
47 | childDom.style.height = '0'
48 | childDom.style.visibility = 'hidden'
49 | iconDom.classList.add('fa-square-plus')
50 | iconDom.classList.remove('fa-square-minus')
51 | }
52 | }
53 | })
54 | })
55 | }
56 |
57 | resetCategoryDom(
58 | document.querySelectorAll('.site-all-category-list .site-all-category-list-item')
59 | )
60 |
61 | expandHandle()
62 | }
63 |
64 | if (KEEP.theme_config.pjax.enable === true && KEEP.utils) {
65 | resetCategoriesPage()
66 | } else {
67 | window.addEventListener('DOMContentLoaded', resetCategoriesPage)
68 | }
69 |
--------------------------------------------------------------------------------
/source/js/page/home-page.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | function homePageHandler() {
4 | const { post_datetime, post_datetime_format, announcement } = KEEP.theme_config?.home || {}
5 | const fsc = KEEP.theme_config?.first_screen || {}
6 |
7 | // reset home post update datetime
8 | const resetHomePostUpdateDate = () => {
9 | if (post_datetime === 'updated' && post_datetime_format) {
10 | const datetimeDoms = document.querySelectorAll('.post-meta-info .home-post-history')
11 | datetimeDoms.forEach((datetimeDom) => {
12 | const updated = new Date(datetimeDom.dataset.updated).getTime()
13 | const format = post_datetime_format || KEEP.themeInfo.defaultDatetimeFormat
14 | datetimeDom.innerHTML = KEEP.utils.formatDatetime(format, updated)
15 | })
16 | }
17 | }
18 |
19 | // set how long age in home post block
20 | const setHowLongAgoInHome = () => {
21 | if (post_datetime_format && post_datetime_format !== 'ago') {
22 | return
23 | }
24 | const datetimeDoms = document.querySelectorAll('.post-meta-info .home-post-history')
25 | datetimeDoms.forEach((v) => {
26 | const nowTimestamp = Date.now()
27 | const updatedTimestamp = new Date(v.dataset.updated).getTime()
28 | v.innerHTML = KEEP.utils.getHowLongAgo(Math.floor((nowTimestamp - updatedTimestamp) / 1000))
29 | })
30 | }
31 |
32 | // close website announcement
33 | const closeWebsiteAnnouncement = () => {
34 | if (announcement) {
35 | const waDom = document.querySelector('.home-content-container .website-announcement')
36 | if (waDom) {
37 | const closeDom = waDom.querySelector('.close')
38 | closeDom.addEventListener('click', () => {
39 | waDom.style.display = 'none'
40 | })
41 | }
42 | }
43 | }
44 |
45 | // first screen typewriter
46 | const initTypewriter = () => {
47 | const isHitokoto = fsc?.hitokoto === true
48 |
49 | if (fsc?.enable !== true) {
50 | return
51 | }
52 |
53 | if (fsc?.enable === true && !isHitokoto && !fsc?.description) {
54 | return
55 | }
56 |
57 | const descBox = document.querySelector('.first-screen-content .description')
58 | if (descBox) {
59 | descBox.style.opacity = '0'
60 |
61 | setTimeout(
62 | () => {
63 | descBox.style.opacity = '1'
64 | const descItemList = descBox.querySelectorAll('.desc-item')
65 | descItemList.forEach((descItem) => {
66 | const desc = descItem.querySelector('.desc')
67 | const cursor = descItem.querySelector('.cursor')
68 | const text = desc.innerHTML
69 | desc.innerHTML = ''
70 | let charIndex = 0
71 |
72 | if (text) {
73 | const typewriter = () => {
74 | if (charIndex < text.length) {
75 | desc.textContent += text.charAt(charIndex)
76 | charIndex++
77 | setTimeout(typewriter, 100)
78 | } else {
79 | cursor.style.display = 'none'
80 | }
81 | }
82 |
83 | typewriter()
84 | }
85 | })
86 | },
87 | isHitokoto ? 400 : 300
88 | )
89 | }
90 | }
91 |
92 | resetHomePostUpdateDate()
93 | setHowLongAgoInHome()
94 | closeWebsiteAnnouncement()
95 | initTypewriter()
96 | }
97 |
98 | if (KEEP.theme_config?.pjax?.enable === true && KEEP.utils) {
99 | homePageHandler()
100 | } else {
101 | window.addEventListener('DOMContentLoaded', homePageHandler)
102 | }
103 |
--------------------------------------------------------------------------------
/source/js/page/links-page.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | function linksPageHandle() {
4 | const friendsLinkBoxDom = document.querySelector('.friends-link-list')
5 |
6 | if (!friendsLinkBoxDom) {
7 | return
8 | }
9 |
10 | const linkTypeDoms = friendsLinkBoxDom.querySelectorAll('.link-type-title')
11 | const linkItemDoms = friendsLinkBoxDom.querySelectorAll('.friends-link-item')
12 | const linksCount = linkItemDoms.length
13 | let columns = 2
14 |
15 | if (linksCount >= 80) {
16 | columns = 4
17 | } else if (linksCount >= 60) {
18 | columns = 3
19 | }
20 |
21 | friendsLinkBoxDom.style.gridTemplateColumns = `repeat(${columns}, 1fr)`
22 | linkTypeDoms.forEach((ltd) => {
23 | ltd.style.gridColumn = `span ${columns}`
24 |
25 | let folded = false
26 | const siblings = []
27 | let nextSibling = ltd.nextElementSibling
28 |
29 | while (nextSibling) {
30 | if (nextSibling.classList.contains('friends-link-item')) {
31 | siblings.push(nextSibling)
32 | nextSibling = nextSibling.nextElementSibling
33 | } else {
34 | break
35 | }
36 | }
37 |
38 | const foldDom = ltd.querySelector('.fold')
39 | foldDom.addEventListener('click', () => {
40 | folded = !folded
41 | foldDom.classList.remove(`fa-chevron-${folded ? 'down' : 'left'}`)
42 | foldDom.classList.add(`fa-chevron-${folded ? 'left' : 'down'}`)
43 | siblings.forEach((link) => (link.style.display = folded ? 'none' : 'block'))
44 | })
45 | })
46 | }
47 |
48 | if (KEEP.theme_config?.pjax?.enable === true && KEEP.utils) {
49 | linksPageHandle()
50 | } else {
51 | window.addEventListener('DOMContentLoaded', linksPageHandle)
52 | }
53 |
--------------------------------------------------------------------------------
/source/js/page/photos-page.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | function photosPageHandle() {
4 | const layoutImages = () => {
5 | const gallery = document.querySelector('.photo-album-box')
6 | const images = gallery.querySelectorAll('img')
7 | const columns = 3
8 | gallery.style.columnCount = `${columns}`
9 | const columnHeights = new Array(columns).fill(0)
10 | images.forEach((img) => {
11 | const shortestColumn = columnHeights.indexOf(Math.min(...columnHeights))
12 | img.style.column = shortestColumn + 1
13 | columnHeights[shortestColumn] += img.height
14 | })
15 | }
16 | layoutImages()
17 | }
18 |
19 | if (KEEP.theme_config?.pjax?.enable === true && KEEP.utils) {
20 | photosPageHandle()
21 | } else {
22 | window.addEventListener('DOMContentLoaded', photosPageHandle)
23 | }
24 |
--------------------------------------------------------------------------------
/source/js/page/tools-page.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | function toolsPageHandle() {
4 | const toolsNavBox = document.querySelector('.tools-nav-box')
5 |
6 | if (!toolsNavBox) {
7 | return
8 | }
9 |
10 | const toolItemList = toolsNavBox.querySelector('.tool-item-list')
11 | const toolCategoryAnchorDoms = toolItemList.querySelectorAll('.tool-category-name')
12 | const toolNavList = toolsNavBox.querySelectorAll('.tools-nav-list .tool-nav-category')
13 |
14 | const toolItemHandle = () => {
15 | toolCategoryAnchorDoms.forEach((ltd) => {
16 | let folded = false
17 | const siblings = []
18 | let nextSibling = ltd.nextElementSibling
19 |
20 | while (nextSibling) {
21 | if (nextSibling.classList.contains('tool-item')) {
22 | siblings.push(nextSibling)
23 | nextSibling = nextSibling.nextElementSibling
24 | } else {
25 | break
26 | }
27 | }
28 |
29 | const foldDom = ltd.querySelector('.fold')
30 | foldDom.addEventListener('click', () => {
31 | folded = !folded
32 | foldDom.classList.remove(`fa-chevron-${folded ? 'down' : 'left'}`)
33 | foldDom.classList.add(`fa-chevron-${folded ? 'left' : 'down'}`)
34 | siblings.forEach((link) => (link.style.display = folded ? 'none' : 'block'))
35 | })
36 | })
37 | }
38 | toolItemHandle()
39 |
40 | const clearToolNavActive = () => {
41 | toolNavList.forEach((tn) => tn.classList.remove('active'))
42 | }
43 |
44 | const toolNavHandle = () => {
45 | toolNavList.forEach((tn) => {
46 | const anchor = toolsNavBox.querySelector(`#${tn.dataset.anchor}`)
47 | KEEP.utils.title2Top4HTag(tn, anchor, 300, () => {
48 | clearToolNavActive()
49 | tn.classList.add('active')
50 | })
51 | })
52 |
53 | const toolCategoryAnchorScrollTopList = [...toolCategoryAnchorDoms].map(
54 | (x) => x.getBoundingClientRect().top
55 | )
56 | let headerHeight = KEEP.utils.headerWrapperDom.getBoundingClientRect().height
57 | if (KEEP.utils.isHideHeader) {
58 | headerHeight = 0
59 | }
60 |
61 | window.addEventListener('scroll', () => {
62 | const scrollTop = KEEP.utils.getScrollTop()
63 | toolCategoryAnchorScrollTopList.forEach((st, idx) => {
64 | if (scrollTop + headerHeight > st) {
65 | clearToolNavActive()
66 | toolNavList[idx].classList.add('active')
67 | }
68 | })
69 | })
70 | }
71 |
72 | toolNavHandle()
73 | }
74 |
75 | if (KEEP.theme_config?.pjax?.enable === true && KEEP.utils) {
76 | toolsPageHandle()
77 | } else {
78 | window.addEventListener('DOMContentLoaded', toolsPageHandle)
79 | }
80 |
--------------------------------------------------------------------------------
/source/js/post/copyright-info.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | function initCopyrightInfoHelper() {
4 | KEEP.utils.copyrightInfoHelper = {
5 | // set post link
6 | initSetPostLink() {
7 | const postLinkContentDom = document.querySelector(
8 | '.copyright-info-content .copyright-post-link'
9 | )
10 | postLinkContentDom && (postLinkContentDom.innerHTML = decodeURI(window.location.href))
11 | },
12 |
13 | // copy copyright info
14 | copyCopyrightInfo() {
15 | const cicDom = document.querySelector('.copyright-info-content')
16 |
17 | if (!cicDom) {
18 | return
19 | }
20 |
21 | const copyDom = document.querySelector('.copy-copyright-info')
22 | const copyIcon = copyDom.querySelector('i')
23 |
24 | const ccLang = KEEP.language_copy_copyright
25 | const colon = KEEP.hexo_config?.language === 'en' ? ': ' : ':'
26 |
27 | let isCopied = false
28 |
29 | const setCopyDomContent = (class1, class2, content, copied) => {
30 | if (copyIcon) {
31 | copyIcon.classList.remove(class1)
32 | copyIcon.classList.add(class2)
33 | }
34 | const tooltipDom = copyDom.querySelector('.tooltip-content')
35 | tooltipDom && (tooltipDom.innerHTML = content)
36 | isCopied = copied
37 | }
38 |
39 | copyDom.addEventListener('click', () => {
40 | if (!isCopied) {
41 | const author = cicDom.querySelector('.copyright-post-author .content').innerHTML
42 | const link = cicDom.querySelector('.copyright-post-link').innerHTML
43 | const tgtTxt = `${ccLang.author}${colon}${author}\n${ccLang.link}${colon}${link}`
44 | navigator.clipboard.writeText(tgtTxt).then(() => {
45 | setCopyDomContent('fa-copy', 'fa-check', ccLang.copied, true)
46 | })
47 | }
48 | })
49 |
50 | copyDom.addEventListener('mouseleave', () => {
51 | setTimeout(() => {
52 | setCopyDomContent('fa-check', 'fa-copy', ccLang.copy, false)
53 | }, 500)
54 | })
55 | }
56 | }
57 |
58 | if (KEEP.theme_config.post?.copyright_info === true) {
59 | KEEP.utils.copyrightInfoHelper.initSetPostLink()
60 | KEEP.utils.copyrightInfoHelper.copyCopyrightInfo()
61 | }
62 | }
63 |
64 | if (KEEP.theme_config.pjax?.enable === true && KEEP.utils) {
65 | initCopyrightInfoHelper()
66 | } else {
67 | window.addEventListener('DOMContentLoaded', initCopyrightInfoHelper)
68 | }
69 |
--------------------------------------------------------------------------------
/source/js/post/share.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | function initPostShareHelper() {
4 | KEEP.utils.postShareHelper = {
5 | postShareHandle() {
6 | const pageUrl = window.location.href
7 | const pageTitle = window.document.title
8 |
9 | const shareContainer = document.querySelector('.post-share-container .share-list-wrap')
10 |
11 | // WeChat share
12 | const wechatShare = shareContainer.querySelector('.wechat')
13 | wechatShare &&
14 | wechatShare.setAttribute(
15 | 'data-tooltip-img-url',
16 | `https://api.qrserver.com/v1/create-qr-code?data=${pageUrl}`
17 | )
18 |
19 | shareContainer.querySelectorAll('.share-item').forEach((item) => {
20 | item.addEventListener('click', () => {
21 | // QQ share
22 | if (item.classList.contains('qq')) {
23 | window.open(`https://connect.qq.com/widget/shareqq/index.html?url=${pageUrl}`)
24 | }
25 |
26 | // WeiBo share
27 | if (item.classList.contains('weibo')) {
28 | window.open(
29 | `https://service.weibo.com/share/share.php?url=${pageUrl}&title=${pageTitle}`
30 | )
31 | }
32 | })
33 | })
34 | }
35 | }
36 |
37 | if (KEEP.theme_config.post?.share === true) {
38 | KEEP.utils.postShareHelper.postShareHandle()
39 | }
40 | }
41 |
42 | if (KEEP.theme_config.pjax?.enable === true && KEEP.utils) {
43 | initPostShareHelper()
44 | } else {
45 | window.addEventListener('DOMContentLoaded', initPostShareHelper)
46 | }
47 |
--------------------------------------------------------------------------------
/source/js/toggle-theme.js:
--------------------------------------------------------------------------------
1 | /* global KEEP */
2 |
3 | KEEP.initModeToggle = () => {
4 | KEEP.utils.modeToggle = {
5 | themeModeToggleBtn: document.querySelector('.tool-toggle-theme-mode'),
6 | iconDom: document.querySelector('.tool-toggle-theme-mode i'),
7 |
8 | enableLightMode() {
9 | document.documentElement.classList.remove('dark-mode')
10 | document.documentElement.classList.add('light-mode')
11 | this.iconDom && (this.iconDom.className = 'fas fa-moon')
12 | KEEP.themeInfo.styleStatus.isDark = false
13 | KEEP.setStyleStatus()
14 | },
15 |
16 | enableDarkMode() {
17 | document.documentElement.classList.add('dark-mode')
18 | document.documentElement.classList.remove('light-mode')
19 | this.iconDom && (this.iconDom.className = 'fas fa-sun')
20 | KEEP.themeInfo.styleStatus.isDark = true
21 | KEEP.setStyleStatus()
22 | },
23 |
24 | isDarkPrefersColorScheme() {
25 | return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)')
26 | },
27 |
28 | initModeStatus() {
29 | const configMode = KEEP.theme_config?.base_info?.mode
30 |
31 | if (configMode === 'dark') {
32 | this.enableDarkMode()
33 | return
34 | }
35 |
36 | if (configMode === 'light') {
37 | this.enableLightMode()
38 | return
39 | }
40 |
41 | const styleStatus = KEEP.getStyleStatus()
42 |
43 | if (styleStatus) {
44 | styleStatus.isDark ? this.enableDarkMode() : this.enableLightMode()
45 | } else {
46 | this.isDarkPrefersColorScheme().matches ? this.enableDarkMode() : this.enableLightMode()
47 | }
48 | },
49 |
50 | initModeToggleButton() {
51 | if (this.themeModeToggleBtn && !this.themeModeToggleBtn?.hasClickListener) {
52 | this.themeModeToggleBtn.addEventListener('click', () => {
53 | const isDark = document.documentElement.classList.contains('dark-mode')
54 | isDark ? this.enableLightMode() : this.enableDarkMode()
55 | })
56 | this.themeModeToggleBtn.hasClickListener = true
57 | }
58 | },
59 |
60 | initModeAutoTrigger() {
61 | const isDarkMode = this.isDarkPrefersColorScheme()
62 | isDarkMode.addEventListener('change', (e) => {
63 | e.matches ? this.enableDarkMode() : this.enableLightMode()
64 | })
65 | }
66 | }
67 |
68 | KEEP.utils.modeToggle.initModeStatus()
69 | KEEP.utils.modeToggle.initModeToggleButton()
70 | KEEP.utils.modeToggle.initModeAutoTrigger()
71 | }
72 |
--------------------------------------------------------------------------------