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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 {};