hexo-theme-shokax 0.3.13 → 0.4.0-alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. package/_config.yml +11 -5
  2. package/layout/_mixin/comment.pug +4 -4
  3. package/package.json +4 -4
  4. package/scripts/filters/locals.d.ts +1 -0
  5. package/scripts/filters/locals.ts +59 -0
  6. package/scripts/filters/post.d.ts +0 -0
  7. package/scripts/filters/post.ts +6 -0
  8. package/scripts/generaters/archive.d.ts +1 -0
  9. package/scripts/generaters/archive.ts +144 -0
  10. package/scripts/generaters/config.d.ts +1 -0
  11. package/scripts/generaters/config.ts +52 -0
  12. package/scripts/generaters/images.d.ts +1 -0
  13. package/scripts/generaters/images.ts +26 -0
  14. package/scripts/generaters/index.d.ts +1 -0
  15. package/scripts/generaters/index.ts +110 -0
  16. package/scripts/generaters/pages.d.ts +0 -0
  17. package/scripts/generaters/pages.ts +16 -0
  18. package/scripts/generaters/script.d.ts +1 -0
  19. package/scripts/generaters/script.js +22 -6
  20. package/scripts/generaters/script.ts +110 -0
  21. package/scripts/helpers/asset.d.ts +1 -0
  22. package/scripts/helpers/asset.ts +158 -0
  23. package/scripts/helpers/engine.d.ts +1 -0
  24. package/scripts/helpers/engine.ts +171 -0
  25. package/scripts/helpers/list_categories.d.ts +1 -0
  26. package/scripts/helpers/list_categories.ts +104 -0
  27. package/scripts/helpers/summary_ai.d.ts +1 -0
  28. package/scripts/helpers/summary_ai.ts +100 -0
  29. package/scripts/helpers/symbols_count_time.d.ts +1 -0
  30. package/scripts/helpers/symbols_count_time.ts +76 -0
  31. package/scripts/plugin/check.d.ts +1 -0
  32. package/scripts/plugin/check.ts +35 -0
  33. package/scripts/plugin/index.d.ts +6 -0
  34. package/scripts/plugin/index.ts +52 -0
  35. package/scripts/plugin/lib/injects-point.d.ts +5 -0
  36. package/scripts/plugin/lib/injects-point.ts +20 -0
  37. package/scripts/plugin/lib/injects.d.ts +2 -0
  38. package/scripts/plugin/lib/injects.ts +101 -0
  39. package/scripts/tags/links.d.ts +1 -0
  40. package/scripts/tags/links.ts +75 -0
  41. package/scripts/tags/media.d.ts +1 -0
  42. package/scripts/tags/media.ts +19 -0
  43. package/source/js/_app/components/sidebar.ts +237 -0
  44. package/source/js/_app/globals/globalVars.ts +98 -0
  45. package/source/js/_app/globals/handles.ts +122 -0
  46. package/source/js/_app/globals/themeColor.ts +64 -0
  47. package/source/js/_app/globals/thirdparty.ts +63 -0
  48. package/source/js/_app/globals/tools.ts +74 -0
  49. package/source/js/_app/library/anime.ts +109 -0
  50. package/source/js/_app/library/declare.d.ts +117 -0
  51. package/source/js/_app/library/dom.ts +26 -0
  52. package/source/js/_app/library/libtype.d.ts +4 -0
  53. package/source/js/_app/library/loadFile.ts +41 -0
  54. package/source/js/_app/library/proto.ts +143 -0
  55. package/source/js/_app/library/scriptPjax.ts +72 -0
  56. package/source/js/_app/library/storage.ts +12 -0
  57. package/source/js/_app/library/vue.ts +60 -0
  58. package/source/js/_app/page/common.ts +42 -0
  59. package/source/js/_app/page/fancybox.ts +70 -0
  60. package/source/js/_app/page/post.ts +265 -0
  61. package/source/js/_app/page/search.ts +129 -0
  62. package/source/js/_app/page/tab.ts +59 -0
  63. package/source/js/_app/pjax/domInit.ts +95 -0
  64. package/source/js/_app/pjax/refresh.ts +75 -0
  65. package/source/js/_app/pjax/siteInit.ts +67 -0
  66. package/source/js/_app/player.ts +798 -0
  67. package/source/js/_app/components/sidebar.js +0 -209
  68. package/source/js/_app/fireworks.js +0 -10
  69. package/source/js/_app/globals/globalVars.js +0 -80
  70. package/source/js/_app/globals/handles.js +0 -98
  71. package/source/js/_app/globals/themeColor.js +0 -62
  72. package/source/js/_app/globals/thirdparty.js +0 -62
  73. package/source/js/_app/globals/tools.js +0 -66
  74. package/source/js/_app/library/anime.js +0 -106
  75. package/source/js/_app/library/dom.js +0 -34
  76. package/source/js/_app/library/loadFile.js +0 -36
  77. package/source/js/_app/library/proto.js +0 -163
  78. package/source/js/_app/library/scriptPjax.js +0 -70
  79. package/source/js/_app/library/storage.js +0 -12
  80. package/source/js/_app/library/vue.js +0 -53
  81. package/source/js/_app/page/comment.js +0 -23
  82. package/source/js/_app/page/common.js +0 -41
  83. package/source/js/_app/page/fancybox.js +0 -65
  84. package/source/js/_app/page/post.js +0 -244
  85. package/source/js/_app/page/search.js +0 -118
  86. package/source/js/_app/page/tab.js +0 -53
  87. package/source/js/_app/pjax/domInit.js +0 -76
  88. package/source/js/_app/pjax/refresh.js +0 -52
  89. package/source/js/_app/pjax/siteInit.js +0 -51
  90. package/source/js/_app/player.js +0 -771
@@ -0,0 +1,110 @@
1
+ /* global hexo */
2
+ import env from '../../package.json'
3
+ import * as fs from 'hexo-fs'
4
+ import { buildSync } from 'esbuild'
5
+ import { getVendorLink } from '../utils'
6
+
7
+ hexo.extend.generator.register('script', function (locals) {
8
+ const config = hexo.config
9
+ const theme = hexo.theme.config
10
+
11
+ const siteConfig = {
12
+ version: env.version,
13
+ hostname: config.url,
14
+ root: config.root,
15
+ statics: theme.statics,
16
+ favicon: {
17
+ normal: theme.assets + '/favicon.ico',
18
+ hidden: theme.assets + '/failure.ico'
19
+ },
20
+ darkmode: theme.darkmode,
21
+ auto_dark: theme.auto_dark,
22
+ auto_scroll: theme.auto_scroll,
23
+ js: {
24
+ copy_tex: getVendorLink(hexo, theme.vendors.async_js.copy_tex),
25
+ fancybox: getVendorLink(hexo, theme.vendors.async_js.fancybox)
26
+ },
27
+ css: {
28
+ katex: getVendorLink(hexo, theme.vendors.css.katex),
29
+ mermaid: theme.css + '/mermaid.css',
30
+ fancybox: getVendorLink(hexo, theme.vendors.css.fancybox),
31
+ justifiedGallery: getVendorLink(hexo, theme.vendors.css.justifiedGallery)
32
+ },
33
+ loader: theme.loader,
34
+ search: null,
35
+ outime: {
36
+ enable: theme.outime.enable,
37
+ days: theme.outime.days
38
+ },
39
+ quicklink: {
40
+ timeout: theme.quicklink.timeout,
41
+ priority: theme.quicklink.priority
42
+ },
43
+ playerAPI: theme.playerAPI,
44
+ audio: undefined,
45
+ fireworks: (theme.fireworks && theme.fireworks.enable && theme.fireworks.options)
46
+ ? theme.fireworks.options
47
+ : undefined
48
+ }
49
+
50
+ if (config?.algolia) {
51
+ siteConfig.search = {
52
+ appID: config.algolia.appId,
53
+ apiKey: config.algolia.apiKey,
54
+ indexName: config.algolia.indexName,
55
+ hits: theme.search.hits
56
+ }
57
+ }
58
+
59
+ if (theme?.audio) {
60
+ siteConfig.audio = theme.audio
61
+ }
62
+
63
+ let text: string
64
+ let enterPoint: string
65
+ if (fs.existsSync('themes/shokaX/source/js/_app/pjax/siteInit.ts')) {
66
+ enterPoint = 'themes/shokaX/source/js/_app/pjax/siteInit.ts'
67
+ } else {
68
+ enterPoint = 'node_modules/hexo-theme-shokax/source/js/_app/pjax/siteInit.ts'
69
+ }
70
+ text = 'const CONFIG = ' + JSON.stringify(siteConfig) + ';'
71
+ buildSync({
72
+ entryPoints: [enterPoint],
73
+ bundle: true,
74
+ outfile: 'shokax_temp.js',
75
+ tsconfigRaw: {
76
+ compilerOptions: {
77
+ target: 'ES2022',
78
+ esModuleInterop: true,
79
+ module: 'ESNext',
80
+ moduleResolution: 'Node',
81
+ skipLibCheck: true
82
+ }
83
+ },
84
+ platform: 'browser',
85
+ format: 'iife',
86
+ target: ['es2022'],
87
+ minify: true,
88
+ define: {
89
+ __UNLAZY_LOGGING__: 'false',
90
+ __UNLAZY_HASH_DECODING__: theme.modules.unlazyHash ? 'true' : 'false',
91
+ __shokax_player__: theme.modules.player ? 'true' : 'false',
92
+ __shokax_VL__: theme.modules.visibilityListener ? 'true' : 'false',
93
+ __shokax_fireworks__: (theme.fireworks && theme.fireworks.enable && theme.fireworks.options && theme.modules.fireworks) ? 'true' : 'false',
94
+ __shokax_search__: config?.algolia ? 'true' : 'false',
95
+ __shokax_outime__: theme.outime.enable ? 'true' : 'false',
96
+ __shokax_tabs__: theme.modules.tabs ? 'true' : 'false',
97
+ __shokax_quiz__: theme.modules.quiz ? 'true' : 'false',
98
+ __shokax_fancybox__: theme.modules.fancybox ? 'true' : 'false'
99
+ }
100
+ })
101
+ text += fs.readFileSync('shokax_temp.js')
102
+ const result = hexo.render.renderSync({ text, engine: 'js' })
103
+ fs.unlinkSync('shokax_temp.js')
104
+ return {
105
+ path: theme.js + '/app.js',
106
+ data: function () {
107
+ return result
108
+ }
109
+ }
110
+ })
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,158 @@
1
+ /* global hexo */
2
+
3
+ import type { VendorsConfig } from '../utils'
4
+ import theme_env from '../../package.json'
5
+ import { htmlTag, url_for, stripHTML } from 'hexo-util'
6
+ import { getVendorLink } from '../utils'
7
+
8
+ hexo.extend.helper.register('_new_comments', function (mode) {
9
+ const root = this.config.url.replace(/^(https?:\/\/)?[^\/]*/, '')
10
+ if (mode === 'twikoo') {
11
+ return `<script data-pjax type="module">
12
+ let comments = []
13
+ twikoo.getRecentComments({
14
+ envId: "${hexo.theme.config?.twikoo?.envId}",
15
+ pageSize: 10
16
+ }).then(function (res) {
17
+ res.forEach(function (item) {
18
+ let cText = item.commentText
19
+ if (item.commentText.length > 50) {
20
+ cText = item.commentText.substring(0,50)+'...'
21
+ }
22
+ const siteLink = item.url + "#" + item.id
23
+ comments.push({
24
+ href: siteLink,
25
+ nick: item.nick,
26
+ time: item.relativeTime,
27
+ text: cText
28
+ })
29
+ });
30
+ Vue.createApp({
31
+ data() {
32
+ return {
33
+ coms: comments,
34
+ root: '${root}'
35
+ }
36
+ }
37
+ }).mount('#new-comment')
38
+ }).catch(function (err) {
39
+ console.error(err)
40
+ })
41
+ </script>`
42
+ } else if (mode === 'waline') {
43
+ return `
44
+ <script type="module" data-pjax>
45
+ let items = []
46
+ import { RecentComments } from 'https://npm.webcache.cn/@waline/client@v2/dist/waline.mjs'
47
+ RecentComments({
48
+ serverURL: '${hexo.theme.config.waline.serverURL.replace(/\/+$/, '')}',
49
+ count: 10,
50
+ }).then(({ comments }) => {
51
+ comments.forEach(function (item) {
52
+ let cText = (item.orig.length > 50) ? item.orig.substring(0,50)+'...' : item.orig
53
+ item.url = item.url.startsWith('/') ? item.url : '/' + item.url;
54
+ const siteLink = item.url + "#" + item.objectId
55
+ items.push({
56
+ href: siteLink,
57
+ nick: item.nick,
58
+ time: item.insertedAt.split('T').shift(),
59
+ text: cText
60
+ })
61
+ })
62
+ Vue.createApp({
63
+ data() {
64
+ return {
65
+ coms: items,
66
+ root: '${root}'
67
+ }
68
+ }
69
+ }).mount('#new-comment')
70
+ }).catch(function (err) {
71
+ console.error(err)
72
+ })
73
+ </script>
74
+ `
75
+ } else {
76
+ console.log(`${mode} is not supported recent comment`)
77
+ }
78
+ })
79
+
80
+ hexo.extend.helper.register('_safedump', (source) => {
81
+ return JSON.stringify(source)
82
+ })
83
+
84
+ hexo.extend.helper.register('hexo_env', function (type) {
85
+ return this.env[type]
86
+ })
87
+
88
+ hexo.extend.helper.register('theme_env', function (type) {
89
+ return theme_env[type]
90
+ })
91
+
92
+ hexo.extend.helper.register('_vendor_font', () => {
93
+ const config = hexo.theme.config.font
94
+
95
+ if (!config || !config.enable) return ''
96
+
97
+ const fontDisplay = '&display=swap'
98
+ const fontSubset = '&subset=latin,latin-ext'
99
+ const fontStyles = ':300,300italic,400,400italic,700,700italic'
100
+ const fontHost = 'https://fonts.googleapis.com'
101
+
102
+ // Get a font list from config
103
+ let fontFamilies = ['global', 'logo', 'title', 'headings', 'posts', 'codes'].map(item => {
104
+ if (config[item] && config[item].family && config[item].external) {
105
+ return config[item].family + fontStyles
106
+ }
107
+ return ''
108
+ })
109
+
110
+ fontFamilies = fontFamilies.filter(item => item !== '')
111
+ // @ts-ignore
112
+ fontFamilies = [...new Set(fontFamilies)]
113
+ // @ts-ignore
114
+ fontFamilies = fontFamilies.join('|')
115
+
116
+ // Merge extra parameters to the final processed font string
117
+ return fontFamilies
118
+ ? htmlTag('link', {
119
+ rel: 'stylesheet',
120
+ href: `${fontHost}/css?family=${fontFamilies.concat(fontDisplay, fontSubset)}`
121
+ })
122
+ : ''
123
+ })
124
+
125
+ hexo.extend.helper.register('_css', function (...urls) {
126
+ const { statics, css } = hexo.theme.config
127
+
128
+ return urls.map(url => htmlTag('link', {
129
+ rel: 'stylesheet',
130
+ href: url_for.call(this, `${statics}${css}/${url}?v=${theme_env.version}`)
131
+ })).join('')
132
+ })
133
+
134
+ hexo.extend.helper.register('_js', function (...urls) {
135
+ const { statics, js } = hexo.theme.config
136
+
137
+ return urls.map(url => htmlTag('script', { src: url_for.call(this, `${statics}${js}/${url}?v=${theme_env.version}`) }, '')).join('')
138
+ })
139
+
140
+ hexo.extend.helper.register('vendor_js', function () {
141
+ const vendors = hexo.theme.config.vendors as VendorsConfig
142
+ let res = ''
143
+ for (const jsSync in vendors.js) {
144
+ res += htmlTag('script', { src: getVendorLink(hexo, vendors.js[jsSync]) }, '')
145
+ }
146
+ // for (const jsAsync in vendors.async_js) {
147
+ // res += htmlTag('script', { src: getVendorLink(hexo, vendors.async_js[jsAsync]), async: true }, '')
148
+ // }
149
+ return res
150
+ })
151
+
152
+ hexo.extend.helper.register('_striptags', function (data) {
153
+ return stripHTML(data)
154
+ })
155
+
156
+ hexo.extend.helper.register('_truncate', function (data, end) {
157
+ return data.substring(0, end)
158
+ })
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,171 @@
1
+ 'use strict'
2
+
3
+ // @ts-ignore
4
+ import { htmlTag, url_for } from 'hexo-util'
5
+
6
+ const randomServer = parseInt(String(Math.random() * 4), 10) + 1
7
+
8
+ const randomBG = function (count = 1, image_server:string = null, image_list:string[] = []) {
9
+ let i
10
+ if (image_server) {
11
+ if (count && count > 1) {
12
+ const arr = new Array(count)
13
+ for (i = 0; i < arr.length; i++) {
14
+ arr[i] = image_server + '?' + Math.floor(Math.random() * 999999)
15
+ }
16
+
17
+ return arr
18
+ }
19
+
20
+ return image_server + '?' + Math.floor(Math.random() * 999999)
21
+ }
22
+
23
+ const parseImage = function (img:string, size:string) {
24
+ if (img.startsWith('//') || img.startsWith('http')) {
25
+ return img
26
+ } else if (hexo.theme.config.experiments?.usingRelative) { // support relative url
27
+ return img
28
+ } else {
29
+ console.warn("sinaimg blocked all request from outside website,so don't use this format")
30
+ return `https://tva${randomServer}.sinaimg.cn/` + size + '/' + img
31
+ }
32
+ }
33
+
34
+ if (count && count > 1) {
35
+ let shuffled = image_list.slice(0)
36
+ while (shuffled.length <= 6) {
37
+ shuffled = shuffled.concat(image_list.slice(0))
38
+ }
39
+ i = shuffled.length
40
+ const min = i - count; let temp; let index
41
+ while (i-- > min) {
42
+ index = Math.floor((i + 1) * Math.random())
43
+ temp = shuffled[index]
44
+ shuffled[index] = shuffled[i]
45
+ shuffled[i] = temp
46
+ }
47
+
48
+ return shuffled.slice(min).map(function (img) {
49
+ return parseImage(img, 'large')
50
+ })
51
+ }
52
+
53
+ return parseImage(image_list[Math.floor(Math.random() * image_list.length)], 'mw690')
54
+ }
55
+
56
+ // 注册hexo主题中的URL帮助方法
57
+ hexo.extend.helper.register('_url', function (path, text, options = {}) {
58
+ // 如果未提供URL路径,则返回
59
+ if (!path) { return }
60
+
61
+ let tag = 'a'
62
+ let attrs: { class: string; 'data-url': any; [index:string]:any } = { href: url_for.call(this, path), class: undefined, external: undefined, rel: undefined, 'data-url': undefined }
63
+
64
+ for (const key in options) {
65
+ attrs[key] = options[key]
66
+ }
67
+
68
+ if (attrs.class && Array.isArray(attrs.class)) {
69
+ attrs.class = attrs.class.join(' ')
70
+ }
71
+
72
+ // 返回HTML标记字符串
73
+ return htmlTag(tag, attrs, decodeURI(text), false)
74
+ })
75
+
76
+ hexo.extend.helper.register('_image_url', function (img, path = '') {
77
+ const { statics } = hexo.theme.config
78
+ const { post_asset_folder } = hexo.config
79
+
80
+ if (img.startsWith('//') || img.startsWith('http')) {
81
+ return img
82
+ } else {
83
+ return url_for.call(this, statics + (post_asset_folder ? path : '') + img)
84
+ }
85
+ })
86
+
87
+ hexo.extend.helper.register('_cover', function (item, num?) {
88
+ const { image_server, image_list } = hexo.theme.config
89
+
90
+ if (item.cover) {
91
+ return this._image_url(item.cover, item.path)
92
+ } else if (item.photos && item.photos.length > 0) {
93
+ return this._image_url(item.photos[0], item.path)
94
+ } else {
95
+ return randomBG(num || 1, image_server, image_list)
96
+ }
97
+ })
98
+
99
+ hexo.extend.helper.register('_cover_index', function (item) {
100
+ const { index_images, image_list, image_server } = hexo.theme.config
101
+
102
+ if (item.cover) {
103
+ return this._image_url(item.cover, item.path)
104
+ } else if (item.photos && item.photos.length > 0) {
105
+ return this._image_url(item.photos[0], item.path)
106
+ } else {
107
+ return randomBG(1, image_server, index_images.length === 0 ? image_list : index_images)
108
+ }
109
+ })
110
+
111
+ // 注册hexo主题的永久链接帮助方法
112
+ hexo.extend.helper.register('_permapath', function (str) {
113
+ // 获取hexo的永久链接配置
114
+ const { permalink } = hexo.config
115
+ // 将输入字符串中的'index.html'替换为空字符串
116
+ let url = str.replace(/index\.html$/, '')
117
+ // 如果永久链接不以'.html'结尾,将输入字符串中的'.html'替换为空字符串
118
+ if (!permalink.endsWith('.html')) {
119
+ url = url.replace(/\.html$/, '')
120
+ }
121
+ // 返回处理后的URL字符串
122
+ return url
123
+ })
124
+
125
+ hexo.extend.helper.register('canonical', function () {
126
+ return `<link rel="canonical" href="${this._permapath(this.url)}">`
127
+ })
128
+
129
+ /**
130
+ * Get page path given a certain language tag
131
+ */
132
+ // 注册hexo主题的国际化路径帮助方法
133
+ hexo.extend.helper.register('i18n_path', function (language) {
134
+ // 获取当前页面的path和lang
135
+ const { path, lang } = this.page
136
+ // 如果path以lang开头,则截取掉lang部分,作为基础路径
137
+ const base = path.startsWith(lang) ? path.slice(lang.length + 1) : path
138
+ // 通过调用url_for方法,生成国际化路径
139
+ return url_for.call(this, `${this.languages.indexOf(language) === 0 ? '' : '/' + language}/${base}`)
140
+ })
141
+
142
+ /**
143
+ * Get the language name
144
+ */
145
+ // 注册hexo主题的语言名称帮助方法
146
+ hexo.extend.helper.register('language_name', function (language) {
147
+ // 从主题配置中获取指定语言的名称
148
+ // @ts-ignore
149
+ const name = hexo.theme.i18n.__(language)('name')
150
+ // 如果名称为默认值'name',则返回语言代码,否则返回语言名称
151
+ return name === 'name' ? language : name
152
+ })
153
+
154
+ hexo.extend.helper.register('random_color', function () {
155
+ const arr:number[] = []
156
+ for (let i = 0; i < 3; i++) {
157
+ arr.push(Math.floor(Math.random() * 128 + 128))
158
+ }
159
+ const [r, g, b] = arr
160
+ return `#${
161
+ r.toString(16).length > 1 ? r.toString(16) : '0' + r.toString(16)
162
+ }${g.toString(16).length > 1 ? g.toString(16) : '0' + g.toString(16)}${
163
+ b.toString(16).length > 1 ? b.toString(16) : '0' + b.toString(16)
164
+ }`
165
+ })
166
+
167
+ hexo.extend.helper.register('shokax_inject', function (point) {
168
+ return hexo.theme.config.injects[point]
169
+ .map(item => this.partial(item.layout, item.locals, item.options))
170
+ .join('')
171
+ })
@@ -0,0 +1 @@
1
+ declare const prepareQuery: (categories: any, parent: any) => any;
@@ -0,0 +1,104 @@
1
+ 'use strict'
2
+ /* global hexo */
3
+
4
+ const prepareQuery = (categories, parent) => {
5
+ const query = {
6
+ parent: undefined
7
+ }
8
+
9
+ if (parent) {
10
+ query.parent = parent
11
+ } else {
12
+ query.parent = { $exists: false }
13
+ }
14
+
15
+ return categories.find(query).sort('name', 1).filter(cat => cat.length)
16
+ }
17
+
18
+ hexo.extend.helper.register('_list_categories', function (depth = 0) {
19
+ // let hexo = this
20
+ const categories = this.site.categories
21
+
22
+ if (!categories || !categories.length) return ''
23
+
24
+ const hierarchicalList = (level, parent?) => {
25
+ let result = ''
26
+
27
+ prepareQuery(categories, parent).forEach((cat, i) => {
28
+ let child
29
+
30
+ if (level + 1 < depth) {
31
+ child = hierarchicalList(level + 1, cat._id)
32
+ }
33
+
34
+ const catname = `<a itemprop="url" href="${this.url_for(cat.path)}">${cat.name}</a><small>( ${cat.length} )</small>`
35
+
36
+ switch (level) {
37
+ case 0:
38
+ result += `<div><h2 class="item header">${catname}</h2>`
39
+ break
40
+
41
+ case 1:
42
+ result += `<h3 class="item section">${catname}</h3>`
43
+ break
44
+
45
+ case 2:
46
+ result += `<div class="item normal"><div class="title">${catname}</div></div>`
47
+ break
48
+ }
49
+
50
+ if (child) {
51
+ result += `${child}`
52
+ }
53
+
54
+ if (level === 0) {
55
+ result += '</div>'
56
+ }
57
+ })
58
+
59
+ return result
60
+ }
61
+
62
+ return hierarchicalList(0)
63
+ })
64
+
65
+ hexo.extend.helper.register('_category_prev', function (name) {
66
+ // let hexo = this
67
+ const categories = this.site.categories
68
+ if (!categories || !categories.length) return ''
69
+
70
+ let result = ''
71
+
72
+ categories.find({ name }).forEach((current) => {
73
+ if (current.parent) {
74
+ categories.find({ _id: current.parent }).forEach((cat, i) => {
75
+ result += `<a href="${this.url_for(cat.path)}">${cat.name}</a>`
76
+ })
77
+ }
78
+ })
79
+
80
+ return result
81
+ })
82
+
83
+ hexo.extend.helper.register('_category_posts', function (page) {
84
+ // let hexo = this
85
+ const categories = this.site.categories
86
+ if (!categories || !categories.length || !page.categories || !page.categories.length) return ''
87
+
88
+ let result = ''
89
+ const cat = page.categories.toArray()
90
+
91
+ categories.find({ _id: cat[cat.length - 1]._id }).forEach((category) => {
92
+ if (category.posts) {
93
+ category.posts.sort('date', 1).forEach((post) => {
94
+ let current = ''
95
+ if (post.path === page.path) {
96
+ current = ' class="active"'
97
+ }
98
+ result += `<li ${current}><a href="${this.url_for(post.path)}" rel="bookmark" title="${post.title}">${post.title}</a></li>`
99
+ })
100
+ }
101
+ })
102
+
103
+ return result
104
+ })
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,100 @@
1
+ import fs from 'node:fs'
2
+
3
+ function getContent (post) {
4
+ return post?.raw ?? post?._content ?? post.content
5
+ }
6
+
7
+ let db:object
8
+ function postMessage (path:string, content:string, dbPath:string, startMessage:string) {
9
+ if (fs.existsSync('summary.json')) {
10
+ // @ts-ignore
11
+ db = JSON.parse(fs.readFileSync('summary.json', { encoding: 'utf-8' }))
12
+ } else {
13
+ db = {}
14
+ }
15
+ const config = hexo.theme.config.summary
16
+ if (config.enable) {
17
+ if (typeof db?.[path] !== 'undefined' && typeof db?.[path]?.[dbPath] !== 'undefined') {
18
+ return db[path][dbPath]
19
+ } else {
20
+ if (typeof db?.[path] === 'undefined') {
21
+ db[path] = {}
22
+ } else {
23
+ db[path][dbPath] = ''
24
+ }
25
+ }
26
+ if (config.mode === 'openai') {
27
+ const request = () => {
28
+ fetch(`${config.openai.remote}/v1/chat/completions`, {
29
+ method: 'POST',
30
+ headers: requestHeaders,
31
+ body: JSON.stringify(requestBody)
32
+ }).then((response) => {
33
+ if (!response.ok) {
34
+ throw Error('ERROR: Failed to get summary from Openai API')
35
+ }
36
+ response.json().then((data:object) => {
37
+ // @ts-ignore
38
+ const summary = data.choices[0].message.content
39
+ try {
40
+ db[path][dbPath] = summary
41
+ } catch (e) {
42
+ db ??= {}
43
+ db[path] ??= {}
44
+ db[path][dbPath] ??= ''
45
+ db[path][dbPath] = summary
46
+ }
47
+ fs.writeFileSync('summary.json', JSON.stringify(db))
48
+ if (fs.existsSync('requested.lock')) {
49
+ fs.unlinkSync('requested.lock')
50
+ }
51
+ return summary
52
+ })
53
+ })
54
+ }
55
+
56
+ const checkTime = (waitTime:number) => {
57
+ if (fs.existsSync('request.lock')) {
58
+ if (fs.existsSync('requested.lock')) {
59
+ setTimeout(checkTime, 1000 * waitTime)
60
+ return
61
+ }
62
+ // Openai API 针对个人用户免费试用限制 3 RPM,这里是25s后发送请求
63
+ fs.writeFileSync('requested.lock', '')
64
+ setTimeout(request, 1000 * 2.5 * waitTime)
65
+ fs.unlinkSync('request.lock')
66
+ } else {
67
+ fs.writeFileSync('request.lock', '')
68
+ request()
69
+ }
70
+ }
71
+ const requestHeaders = {
72
+ 'Content-Type': 'application/json',
73
+ Authorization: `Bearer ${config.openai.apikey}`
74
+ }
75
+ const requestBody = {
76
+ model: 'gpt-3.5-turbo',
77
+ messages: [{ role: 'user', content: `${startMessage} ${content}` }],
78
+ temperature: 0.7
79
+ }
80
+ if (config.pricing === 'trial') {
81
+ hexo.log.info('Requesting OpenAI API... (3 RPM mode)')
82
+ hexo.log.info('It may take 20 minutes or more (depending on the number of articles, each one takes 25 seconds)')
83
+ checkTime(10)
84
+ } else {
85
+ hexo.log.info('Requesting OpenAI API... (60 RPM mode)')
86
+ checkTime(0.5)
87
+ }
88
+ } else {
89
+ // custom尚未支持
90
+ }
91
+ }
92
+ }
93
+
94
+ hexo.extend.helper.register('get_summary', (post) => {
95
+ return postMessage(post.path, getContent(post), 'summary', '请为下述文章提供一份200字以内的概括,使用中文回答且尽可能简洁: ')
96
+ })
97
+
98
+ hexo.extend.helper.register('get_introduce', () => {
99
+ return hexo.theme.config.summary.introduce
100
+ })
@@ -0,0 +1 @@
1
+ export {};