hexo-theme-shokax 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -25,15 +25,17 @@ shokaX的社区资源导航和插件仓库为[awesome-shokaX](https://github.com
25
25
  | PWA支持 | ✅ | JSD拆分 | ✅ |
26
26
  | 注入API | ✅ | 社区插件系统 | ✅ |
27
27
  | 自定义字体 | ✅* | 自定义样式 | ✅* |
28
- | 多种评论系统支持 | ✅ | 用户行为分析支持 | |
28
+ | 多种评论系统支持 | ✅ | AI生成文章概括 | 🔬 |
29
29
  | 底部备案号 | ✅ | 自定义页尾 | ✅* |
30
30
  | CSS渐变封面 | ✅ | typescript支持 | ✅ |
31
31
 
32
32
  备注:
33
33
  - *: 需要使用注入API实现
34
+ - 🔬: 实验中,可能存在问题
34
35
 
35
36
 
36
37
  ## 🔧 如何安装?
38
+ 注意: 本项目需要 node.js 18.x 或更高版本才能运行 \
37
39
  建议使用[ShokaX-CLI](https://github.com/zkz098/shokaX-CLI) ,执行下列命令即可:
38
40
  ```bash
39
41
  npm i shokax-cli --location=global
@@ -58,7 +60,7 @@ github仓库建议通过右边的 releases 下载,步骤为:
58
60
  - [Easy hexo](https://easyhexo.com/)
59
61
 
60
62
  # 许可证
61
- 许可证: GPL 3 \
63
+ 许可证: GPL 3 or later \
62
64
  [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fzkz098%2Fhexo-theme-shokaX.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fzkz098%2Fhexo-theme-shokaX?ref=badge_large)
63
65
 
64
66
  ## 特别说明
@@ -77,3 +79,6 @@ GPL许可证主要目的是限制修改后的分发行为,避免未经许可
77
79
 
78
80
  ## 特别鸣谢
79
81
  [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png" width="25%">](https://jb.gg/OpenSourceSupport)
82
+
83
+ ## 其他信息
84
+ ![Star history chart](https://api.star-history.com/svg?repos=theme-shoka-x/hexo-theme-shokaX&type=Date)
package/README_en.MD CHANGED
@@ -1,25 +1,56 @@
1
+ If the repository address you are visiting is zkz098/hexo-theme-shokaX, please switch to the latest address: theme-shoka-x/hexo-theme-shokaX.
2
+
1
3
  # hexo-theme-shokaX
2
4
  [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fzkz098%2Fhexo-theme-shokaX.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fzkz098%2Fhexo-theme-shokaX?ref=badge_shield)
3
- ![LICENSE]( https://img.shields.io/github/license/zkz098/hexo-theme-shokaX)
4
- ![stars](https://img.shields.io/github/stars/zkz098/hexo-theme-shokaX)
5
+ ![LICENSE]( https://img.shields.io/github/license/theme-shoka-x/hexo-theme-shokaX)
6
+ ![stars](https://img.shields.io/github/stars/theme-shoka-x/hexo-theme-shokaX)
5
7
  ![version](https://shields.io/npm/v/hexo-theme-shokax)
8
+ ![build](https://img.shields.io/github/actions/workflow/status/theme-shoka-x/hexo-theme-shokaX/build-theme.yml)
9
+
10
+ Language: [简体中文](./README.md) | English \
11
+ This project is a secondary development version of shoka (spiritual sequel), dedicated to improving performance and optimizing modding experience.
12
+ The reason for its birth is that shoka has not been updated for two years, with a large backlog of bugs and feature requests.
13
+
14
+ The community resource navigation and plugin repository for shokaX is awesome-shokaX.
15
+
16
+ ## 💬 Differences with shoka
17
+ The original shoka used javascript+Native+nunjucks technology, while shokaX uses typescript+Vue 3+Pug technology and has changed a lot of hard-to-access CDN links.
18
+
19
+ ## ✨ Feature List
20
+ | Feature Name | Implementation Status | Feature Name | Implementation Status |
21
+ |:-------------------------------:|:---------------------:|:------------------------------:|:---------------------:|
22
+ | PWA Support | ✅ | JSD Splitting | ✅ |
23
+ | Injection API | ✅ | Community Plugin System | ✅ |
24
+ | Custom Fonts | ✅* | Custom Styles | ✅* |
25
+ | Multiple Comment System Support | ✅ | User Behavior Analysis Support | ✅ |
26
+ | Record Number at the Bottom | ✅ | Custom Footer | ✅* |
27
+ | CSS Gradient Cover | ✅ | Typescript Support | ✅ |
6
28
 
7
- 语言(language): [简体中文](./README.md) | English \
8
- This project is a secondary development version of Shoka, dedicated to improving performance and optimizing the development experience. \
9
- This project is in a period of intensive development, but the github repository version is basically available. \
10
- Please refer to the wiki for secondary development and common problems.
11
- Starting with `0.0.2-alpha2`, `lantern` and `qweather` have been migrated as plugins.
12
- The plugin system is complete, see [awesome-shokaX](https://github.com/zkz098/awesome-shokaX) for how to use it.
29
+ Remarks:
30
+ - *: Requires implementation using Injection API.
13
31
 
14
- ## How to install?
15
- [ShokaX-CLI](https://github.com/zkz098/shokaX-CLI) is recommended:
32
+ ## 🔧 How to Install?
33
+ It is recommended to use [ShokaX-CLI](https://github.com/zkz098/shokaX-CLI) and execute the following command:
16
34
  ```bash
17
35
  npm i shokax-cli --location=global
18
- # hexo init
19
- SXC install -r=github shokaX
36
+ # hexo init initializes the environment
37
+ SXC install shokaX
20
38
  ```
39
+ [Click here](https://docs.kaitaku.xyz/guide/#%E9%85%8D%E7%BD%AE%E4%B8%BB%E9%A2%98) for the next configuration steps.
40
+
41
+ It is recommended to download the GitHub repository from the Releases on the right-hand side, as follows:
42
+
43
+ - Click on the Latest version in Releases
44
+ - Download the Source code(zip) in Assets
45
+ - Unzip to use as the theme
21
46
 
22
47
  # License
23
- License: BSD-3-Clause \
48
+ license: GPL 3 or later
24
49
  [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fzkz098%2Fhexo-theme-shokaX.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fzkz098%2Fhexo-theme-shokaX?ref=badge_large)
25
50
 
51
+ ## Contributors
52
+
53
+ [![](https://contributors-img.web.app/image?repo=zkz098/hexo-theme-shokaX)](https://github.com/zkz098/hexo-theme-shokaX/graphs/contributors)
54
+
55
+ ## Special thanks
56
+ [<img src="https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.png" width="25%">](https://jb.gg/OpenSourceSupport)
package/_config.yml CHANGED
@@ -198,6 +198,18 @@ giscus:
198
198
  commentTheme:
199
199
  lang:
200
200
 
201
+ summary:
202
+ enable: false
203
+ introduce: "我是基于ChatGPT-turbo-3.5实现的AI助手,在此网站上负责整理和概括文章" # AI自我介绍
204
+ mode: openai # openai/custom
205
+ pricing: "trial" # trial为试用模板(3 RPM);pay为即用即付模板(60 RPM)
206
+ openai:
207
+ remote: "https://api.openai.com"
208
+ apikey: "key"
209
+ custom:
210
+ remote: "http://localhost:8000"
211
+
212
+
201
213
  # Social Links
202
214
  # Usage: `Key: permalink || icon || color`
203
215
  # Key is the link label showing to end users.
@@ -340,7 +352,7 @@ advVendors:
340
352
  pace:
341
353
  src: bytedance:pace/1.0.2/pace.min.js
342
354
  pjax:
343
- src: baomitu:pjax/0.2.8/pjax.min.js
355
+ src: npm:theme-shokax-pjax@latest/pjax.shokax.min.js
344
356
  fetch:
345
357
  src: npm:whatwg-fetch@3.4.0/dist/fetch.umd.js
346
358
  anime:
@@ -392,7 +404,7 @@ vendors:
392
404
 
393
405
  js:
394
406
  pace: npm/pace-js@1.0.2/pace.min.js # ok
395
- pjax: npm/pjax@0.2.8/pjax.min.js # ok
407
+ pjax: npm/theme-shokax-pjax@latest/pjax.shokax.min.js # ok
396
408
  fetch: npm/whatwg-fetch@3.4.0/dist/fetch.umd.min.js # ok
397
409
  anime: npm/theme-shokax-anime@latest/anime.shokax.min.js
398
410
  algolia: npm/algoliasearch@4/dist/algoliasearch-lite.umd.js # ok
@@ -1,6 +1,5 @@
1
1
  mixin PMRender(item, full)
2
2
  - full ||= false
3
- //- 下方的_content为暴力修复法,在未来需要重写
4
3
  div(class="meta")
5
4
  - var create_title = __('post.created') + __('symbol.colon') + full_date(item.date)
6
5
  span(class="item" title=create_title)
@@ -12,6 +12,19 @@ article(itemscope itemtype="http://schema.org/Article" class="post block" lang=t
12
12
  div(class="gallery" itemscope itemtype="http://schema.org/ImageGallery")
13
13
  each photo in post.photos
14
14
  img(data-src=_image_url(photo, post.path) itemprop="contentUrl")
15
+ if theme.summary.enable && page.layout === 'post'
16
+ div(class='tabs' id='summary')
17
+ div(class="show-btn")
18
+ div(class="nav")
19
+ ul(class="special")
20
+ div(class="tab" data-id="summary" data-title="自我介绍")
21
+ p
22
+ != get_introduce()
23
+ div(class="tab active" data-id="summary" data-title="文章概括")
24
+ p
25
+ != get_summary(page)
26
+
27
+
15
28
  != post.content
16
29
  if post.tags && post.tags.length
17
30
  div(class="tags")
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "hexo-theme-shokax",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "a hexo theme based on shoka",
5
5
  "main": "index.js",
6
- "repository": "https://github.com/zkz098/hexo-theme-shokaX",
6
+ "repository": "https://github.com/theme-shoka-x/hexo-theme-shokaX",
7
7
  "author": "Chou kaitaku",
8
8
  "license": "GPL-3.0-or-later",
9
9
  "scripts": {
@@ -34,12 +34,12 @@
34
34
  "hexo-fs": "^4.1.1",
35
35
  "hexo-util": "^3.0.1",
36
36
  "instantsearch.js": "^4.54.1",
37
- "pjax": "^0.2.8",
38
37
  "theme-shokax-anime": "^0.0.4",
38
+ "theme-shokax-pjax": "^0.0.2",
39
39
  "typescript": "^5.0.4",
40
40
  "vue": "^3.2.47",
41
41
  "vuepress": "2.0.0-beta.61",
42
- "vuepress-plugin-sitemap2": "2.0.0-beta.205"
42
+ "vuepress-plugin-sitemap2": "2.0.0-beta.206"
43
43
  },
44
44
  "dependencies": {
45
45
  "js-yaml": "^4.1.0",
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_fs_1 = __importDefault(require("node:fs"));
7
+ function getContent(post) {
8
+ return post?.raw ?? post?._content ?? post.content;
9
+ }
10
+ let db;
11
+ function postMessage(path, content, dbPath, startMessage) {
12
+ if (node_fs_1.default.existsSync('summary.json')) {
13
+ db = JSON.parse(node_fs_1.default.readFileSync('summary.json'));
14
+ }
15
+ else {
16
+ db = {};
17
+ }
18
+ const config = hexo.theme.config.summary;
19
+ if (config.enable) {
20
+ if (typeof db?.[path] !== 'undefined' && typeof db?.[path]?.[dbPath] !== 'undefined') {
21
+ return db[path][dbPath];
22
+ }
23
+ else {
24
+ if (typeof db?.[path] === 'undefined') {
25
+ db[path] = {};
26
+ }
27
+ else {
28
+ db[path][dbPath] = '';
29
+ }
30
+ }
31
+ if (config.mode === 'openai') {
32
+ const request = () => {
33
+ fetch(`${config.openai.remote}/v1/chat/completions`, {
34
+ method: 'POST',
35
+ headers: requestHeaders,
36
+ body: JSON.stringify(requestBody)
37
+ }).then((response) => {
38
+ if (!response.ok) {
39
+ throw Error('ERROR: Failed to get summary from Openai API');
40
+ }
41
+ response.json().then((data) => {
42
+ const summary = data.choices[0].message.content;
43
+ try {
44
+ db[path][dbPath] = summary;
45
+ }
46
+ catch (e) {
47
+ db ??= {};
48
+ db[path] ??= {};
49
+ db[path][dbPath] ??= '';
50
+ console.log(db[path]);
51
+ db[path][dbPath] = summary;
52
+ }
53
+ node_fs_1.default.writeFileSync('summary.json', JSON.stringify(db));
54
+ if (node_fs_1.default.existsSync('requested.lock')) {
55
+ node_fs_1.default.unlinkSync('requested.lock');
56
+ }
57
+ return summary;
58
+ });
59
+ });
60
+ };
61
+ const checkTime = (waitTime) => {
62
+ if (node_fs_1.default.existsSync('request.lock')) {
63
+ if (node_fs_1.default.existsSync('requested.lock')) {
64
+ setTimeout(checkTime, 1000 * waitTime);
65
+ return;
66
+ }
67
+ node_fs_1.default.writeFileSync('requested.lock', '');
68
+ setTimeout(request, 1000 * 2.5 * waitTime);
69
+ node_fs_1.default.unlinkSync('request.lock');
70
+ }
71
+ else {
72
+ node_fs_1.default.writeFileSync('request.lock', '');
73
+ request();
74
+ }
75
+ };
76
+ const requestHeaders = {
77
+ 'Content-Type': 'application/json',
78
+ Authorization: `Bearer ${config.openai.apikey}`
79
+ };
80
+ const requestBody = {
81
+ model: 'gpt-3.5-turbo',
82
+ messages: [{ role: 'user', content: `${startMessage} ${content}` }],
83
+ temperature: 0.7
84
+ };
85
+ if (config.pricing === 'trial') {
86
+ hexo.log.info('Requesting OpenAI API... (3 RPM mode)');
87
+ hexo.log.info('It may take 20 minutes or more (depending on the number of articles, each one takes 25 seconds)');
88
+ checkTime(10);
89
+ }
90
+ else {
91
+ hexo.log.info('Requesting OpenAI API... (60 RPM mode)');
92
+ checkTime(0.5);
93
+ }
94
+ }
95
+ else {
96
+ }
97
+ }
98
+ }
99
+ hexo.extend.helper.register('get_summary', (post) => {
100
+ return postMessage(post.path, getContent(post), 'summary', '请为下述文章提供一份200字以内的概括,使用中文回答且尽可能简洁: ');
101
+ });
102
+ hexo.extend.helper.register('get_introduce', () => {
103
+ return hexo.theme.config.summary.introduce;
104
+ });
@@ -6,8 +6,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const injects_1 = __importDefault(require("./lib/injects"));
7
7
  const node_https_1 = __importDefault(require("node:https"));
8
8
  const package_json_1 = require("../../package.json");
9
+ const node_fs_1 = __importDefault(require("node:fs"));
9
10
  hexo.on('generateBefore', () => {
10
11
  (0, injects_1.default)(hexo);
12
+ if (node_fs_1.default.existsSync('request.lock')) {
13
+ node_fs_1.default.unlinkSync('request.lock');
14
+ }
15
+ if (node_fs_1.default.existsSync('requested.lock')) {
16
+ node_fs_1.default.unlinkSync('requested.lock');
17
+ }
11
18
  });
12
19
  hexo.on('generateAfter', () => {
13
20
  node_https_1.default.get('https://api.github.com/repos/theme-shoka-x/hexo-theme-shokaX/releases/latest', {
@@ -14,6 +14,13 @@
14
14
  overflow-x: auto;
15
15
  }
16
16
 
17
+ ul.special::before {
18
+ font-family: ic;
19
+ content: "\e652";
20
+ font-size: x-large;
21
+ padding-left: 1em;
22
+ }
23
+
17
24
  li {
18
25
  position: relative;
19
26
  cursor: pointer;
@@ -27,20 +34,20 @@
27
34
  position: absolute;
28
35
  left: 50%;
29
36
  right: 50%;
30
- top: auto;
37
+ top: 2em;
31
38
  bottom: 0;
32
39
  transition: all .2s ease-in-out;
33
40
  width: auto;
34
41
  height: auto;
35
42
  background: none;
36
- border-radius: 0;
43
+ border-radius: 10px;
37
44
  border-bottom: .125rem solid transparent;
38
45
  }
39
46
 
40
47
  &.active::before {
41
48
  left: 0;
42
49
  right: 0;
43
- border-bottom-color: var(--note-hover, var(--primary-color));
50
+ border-bottom-color: var(--primary-color);
44
51
  }
45
52
  }
46
53
  }
@@ -61,7 +61,7 @@ Object.assign(HTMLElement.prototype, {
61
61
  }
62
62
  return this.getBoundingClientRect().width;
63
63
  },
64
- top: function () {
64
+ getTop: function () {
65
65
  return this.getBoundingClientRect().top;
66
66
  },
67
67
  left: function () {
@@ -296,7 +296,7 @@ const pageScroll = function (target, offset, complete) {
296
296
  targets: typeof offset === 'number' ? target.parentNode : document.scrollingElement,
297
297
  duration: 500,
298
298
  easing: 'easeInOutQuad',
299
- scrollTop: offset || (typeof target === 'number' ? target : (target ? target.top() + document.documentElement.scrollTop - siteNavHeight : 0)),
299
+ scrollTop: offset || (typeof target === 'number' ? target : (target ? target.getTop() + document.documentElement.scrollTop - siteNavHeight : 0)),
300
300
  complete: function () {
301
301
  complete && complete();
302
302
  }
@@ -72,7 +72,7 @@ const postFancybox = function (p) {
72
72
  $dom.each(p + ' p.gallery', function (element) {
73
73
  const box = document.createElement('div');
74
74
  box.className = 'gallery';
75
- box.attr('data-height', element.attr('data-height') || 220);
75
+ box.attr('data-height', String(element.attr('data-height') || 220));
76
76
  box.innerHTML = element.innerHTML.replace(/<br>/g, '');
77
77
  element.parentNode.insertBefore(box, element);
78
78
  element.remove();
@@ -405,7 +405,7 @@ const tabFormat = function () {
405
405
  target.addClass('active');
406
406
  });
407
407
  box.appendChild(element);
408
- element.attr('data-ready', true);
408
+ element.attr('data-ready', String(true));
409
409
  });
410
410
  };
411
411
  const loadComments = function () {
@@ -666,7 +666,6 @@ const siteInit = function () {
666
666
  '.leancloud-recent-comment',
667
667
  'script[data-config]'
668
668
  ],
669
- analytics: false,
670
669
  cacheBust: false
671
670
  });
672
671
  CONFIG.quicklink.ignores = LOCAL.ignores;
@@ -496,7 +496,7 @@ const mediaPlayer = function (t, config) {
496
496
  }
497
497
  playlist.index = index;
498
498
  };
499
- const random = function () {
499
+ const random = () => {
500
500
  const p = utils.random(total);
501
501
  if (playlist.index !== p) {
502
502
  playlist.index = p;
@@ -532,7 +532,7 @@ const mediaPlayer = function (t, config) {
532
532
  this.init();
533
533
  }
534
534
  },
535
- init: () => {
535
+ init: function () {
536
536
  const item = playlist.current();
537
537
  if (!item || item.error) {
538
538
  this.mode();
@@ -608,7 +608,7 @@ const mediaPlayer = function (t, config) {
608
608
  el: null,
609
609
  data: null,
610
610
  index: 0,
611
- create: (box) => {
611
+ create: function (box) {
612
612
  const current = playlist.index;
613
613
  const raw = playlist.current().lrc;
614
614
  const callback = function (body) {