hexo-theme-shokax 0.5.0-beta1 → 0.5.0-beta2-dev-ec59668

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.
package/README.md CHANGED
@@ -5,7 +5,6 @@
5
5
  - [x] 移除 pjax
6
6
  - [x] 移除 quicklink
7
7
  - [x] 移除 assetUrl 为基的动态 Vendor 机制
8
- - [x] 移除 ShokaX Inject
9
8
  - [ ] 引入新的 Inject 类技术 (长期)
10
9
  - [ ] 引入新的工作流程
11
10
  - [x] 重构 player (nyx-player)
package/_config.yml CHANGED
@@ -354,10 +354,6 @@ vendors:
354
354
  cdns:
355
355
  cdnjs: https://s4.zstatic.net/ajax/libs
356
356
  js:
357
- pace:
358
- source: cdnjs
359
- url: pace/1.2.4/pace.min.js
360
- sri: "sha384-k6YtvFUEIuEFBdrLKJ3YAUbBki333tj1CSUisai5Cswsg9wcLNaPzsTHDswp4Az8"
361
357
  jquery:
362
358
  source: cdnjs
363
359
  url: jquery/3.5.1/jquery.min.js
@@ -388,10 +384,6 @@ vendors:
388
384
  source: cdnjs
389
385
  url: KaTeX/0.16.9/katex.min.css
390
386
  sri: "sha384-n8MVd4RsNIU0tAv4ct0nTaAbDJwPJzDEaqSD1odI+WdtXRGWt2kTvGFasHpSy3SV"
391
- comment:
392
- source: local
393
- url: css/comment.css
394
- sri: ""
395
387
  fancybox:
396
388
  source: cdnjs
397
389
  url: fancybox/3.5.7/jquery.fancybox.min.css
@@ -1,4 +1,5 @@
1
1
  mixin CommentRender()
2
+ != shokax_inject('comment')
2
3
  if page.comment !== false
3
4
  div(class="wrap" id="comments")
4
5
 
@@ -37,4 +37,7 @@ div(class="status")
37
37
  a(target="_blank" href=`https://beian.mps.gov.cn/#/query/webSearch?code=${RC}`)
38
38
  img(loading="lazy" decoding="async" src=theme.statics + theme.assets + '/' + theme.footer.icp.icon style="max-width: 2em;display:inline;" width="20" height="20" alt="备案")
39
39
  != beianN
40
+ != shokax_inject('status')
41
+
42
+ != shokax_inject('footer')
40
43
 
@@ -14,4 +14,5 @@ nav(id="nav")
14
14
  i(class="ic i-sun")
15
15
  li(class="item search")
16
16
  i(class="ic i-search")
17
+ != shokax_inject('rightNav')
17
18
 
@@ -21,6 +21,7 @@ html(lang=page.language?page.language:config.language, style=theme.grayMode ? 'f
21
21
  link(rel="preload" href!=covers as="image" fetchpriority="high")
22
22
 
23
23
  != partial('_partials/head/head_com.pug')
24
+ != shokax_inject('head')
24
25
  block head
25
26
  title
26
27
  block title
@@ -130,7 +131,6 @@ html(lang=page.language?page.language:config.language, style=theme.grayMode ? 'f
130
131
  != partial('_partials/third-party/clarity.pug', {}, {cache: true})
131
132
  != partial('_partials/third-party/google-analytics.pug', {}, {cache: true})
132
133
 
133
- != vendor_js('pace')
134
134
  != vendor_js('jquery')
135
135
  != vendor_js('justifiedGallery')
136
136
  != vendor_js('fancybox')
@@ -140,4 +140,6 @@ html(lang=page.language?page.language:config.language, style=theme.grayMode ? 'f
140
140
 
141
141
  != _js('siteInit.js')
142
142
 
143
+ != shokax_inject('bodyEnd')
144
+
143
145
 
@@ -16,3 +16,5 @@ div(class="meta")
16
16
  != __('post.edited') + __('symbol.space')
17
17
  time(title=__('post.modified') + __('symbol.colon') + full_date(post.updated) itemprop="dateModified" datetime=moment(post.updated).format())
18
18
  != date(post.updated)
19
+
20
+ != shokax_inject('postMeta')
@@ -38,3 +38,5 @@ article(itemscope itemtype="http://schema.org/Article" class="post block" lang=t
38
38
  != partial('_partials/post/reward.pug', {}, {cache: true})
39
39
  if theme.creative_commons.license
40
40
  != partial('_partials/post/copyright.pug')
41
+
42
+ != shokax_inject('postBodyEnd')
@@ -36,5 +36,7 @@ div(class="social")
36
36
  - var sidebarIcon = '<i class="ic i-' + link.split('||')[1].trim() + '"></i>'
37
37
  != _url(sidebarURL, sidebarIcon, {title: sidebarURL, class: 'item ' + name})
38
38
 
39
+ != shokax_inject('sidebar')
40
+
39
41
  div(class="menu")
40
42
  != partial('_partials/sidebar/menu.pug')
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "hexo-theme-shokax",
3
- "version": "0.5.0-beta1",
3
+ "version": "0.5.0-beta2-dev-ec59668",
4
4
  "description": "a hexo theme based on shoka",
5
5
  "main": "index.js",
6
6
  "repository": "https://github.com/theme-shoka-x/hexo-theme-shokaX",
7
7
  "author": "zkz098",
8
8
  "license": "AGPL-3.0-or-later",
9
- "packageManager": "pnpm@10.6.5",
9
+ "packageManager": "pnpm@10.8.0",
10
10
  "scripts": {
11
11
  "test": "tsc --build --verbose",
12
12
  "build": "node ./toolbox/compiler.mjs"
@@ -16,24 +16,24 @@
16
16
  "@types/jquery": "^3.5.32",
17
17
  "@types/js-yaml": "^4.0.9",
18
18
  "@types/katex": "^0.16.7",
19
- "@types/node": "^22.13.10",
19
+ "@types/node": "^22.14.0",
20
20
  "@types/quicklink": "^2.3.4",
21
- "@typescript-eslint/eslint-plugin": "^8.27.0",
22
- "@typescript-eslint/parser": "^8.27.0",
23
- "eslint": "^9.22.0",
21
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
22
+ "@typescript-eslint/parser": "^8.29.1",
23
+ "eslint": "^9.24.0",
24
24
  "eslint-config-standard": "~17",
25
25
  "eslint-plugin-vue": "^10.0.0",
26
26
  "glob": "^11.0.1",
27
- "typescript": "^5.8.2"
27
+ "typescript": "^5.8.3"
28
28
  },
29
29
  "dependencies": {
30
- "@algolia/client-search": "^5.21.0",
30
+ "@algolia/client-search": "^5.23.3",
31
31
  "@common.js/p-limit": "^6.1.0",
32
32
  "@waline/client": "^3.5.6",
33
- "algoliasearch": "5.21.0",
34
- "dompurify": "^3.2.4",
35
- "es-toolkit": "^1.33.0",
36
- "esbuild": "^0.25.1",
33
+ "algoliasearch": "5.23.3",
34
+ "dompurify": "^3.2.5",
35
+ "es-toolkit": "^1.34.1",
36
+ "esbuild": "^0.25.2",
37
37
  "hexo": "^7.3.0",
38
38
  "hexo-algoliasearch": "^2.0.1",
39
39
  "hexo-feed": "^1.1.2",
@@ -41,14 +41,14 @@
41
41
  "hexo-pagination": "^4.0.0",
42
42
  "hexo-renderer-pug": "^3.0.0",
43
43
  "hexo-util": "^3.3.0",
44
- "instantsearch.js": "^4.78.0",
44
+ "instantsearch.js": "^4.78.1",
45
45
  "js-yaml": "^4.1.0",
46
- "katex": "^0.16.21",
46
+ "katex": "^0.16.22",
47
47
  "mouse-firework": "^0.1.1",
48
- "nyx-player": "^0.0.1",
48
+ "nyx-player": "^0.0.2",
49
49
  "quicklink": "^2.3.0",
50
50
  "theme-shokax-anime": "^0.0.8",
51
- "twikoo": "^1.6.41"
51
+ "twikoo": "^1.6.42"
52
52
  },
53
53
  "engines": {
54
54
  "node": ">=20.0.0"
@@ -24,6 +24,7 @@ var import_package = __toESM(require("../../package.json"));
24
24
  var import_promises = __toESM(require("node:fs/promises"));
25
25
  var import_esbuild = require("esbuild");
26
26
  var import_utils = require("../utils");
27
+ var import_hexo_util = require("hexo-util");
27
28
  hexo.extend.generator.register("script", async function(locals) {
28
29
  const config = hexo.config;
29
30
  const theme = hexo.theme.config;
@@ -97,7 +98,8 @@ hexo.extend.generator.register("script", async function(locals) {
97
98
  enterPoint = "node_modules/hexo-theme-shokax/source/js/_app/pjax/siteInit.ts";
98
99
  patchDir = "node_modules/hexo-theme-shokax/source/js/_app/components/cloudflare.ts";
99
100
  }
100
- await (0, import_esbuild.build)({
101
+ const resultApp = await (0, import_esbuild.build)({
102
+ write: false,
101
103
  entryPoints: [enterPoint],
102
104
  bundle: true,
103
105
  outdir: "shokaxTemp",
@@ -134,27 +136,47 @@ hexo.extend.generator.register("script", async function(locals) {
134
136
  }
135
137
  });
136
138
  const res = [];
137
- (await import_promises.default.readdir("shokaxTemp")).forEach(async (file) => {
138
- const fileContent = await import_promises.default.readFile("shokaxTemp/" + file);
139
- if (file.endsWith(".js")) {
139
+ resultApp.outputFiles.forEach((file) => {
140
+ if (file.path.endsWith(".js")) {
140
141
  res.push({
141
- path: theme.js + "/" + file,
142
- data: fileContent
142
+ path: theme.js + "/" + file.path.split("/").pop(),
143
+ data: file.contents
143
144
  });
144
- } else if (file.endsWith(".css")) {
145
+ } else if (file.path.endsWith(".css")) {
145
146
  res.push({
146
- path: theme.css + "/" + file,
147
- data: fileContent
147
+ path: theme.css + "/" + file.path.split("/").pop(),
148
+ data: file.contents
148
149
  });
149
150
  } else {
150
151
  res.push({
151
- path: theme.statics + "/" + file,
152
- data: fileContent
152
+ path: theme.statics + "/" + file.path.split("/").pop(),
153
+ data: file.contents
153
154
  });
154
155
  }
155
156
  });
157
+ hexo.extend.helper.register("preloadjs", function() {
158
+ const { statics, js } = hexo.theme.config;
159
+ let resultHtml = "";
160
+ res.forEach((file) => {
161
+ if (file.path.endsWith(".js")) {
162
+ resultHtml += (0, import_hexo_util.htmlTag)("link", { rel: "modulepreload", href: import_hexo_util.url_for.call(this, `${statics}${js}/${file}`) }, "");
163
+ }
164
+ });
165
+ return resultHtml;
166
+ });
167
+ hexo.extend.helper.register("load_async_css", function() {
168
+ const { statics, css } = hexo.theme.config;
169
+ let resultHtml = "";
170
+ res.forEach((file) => {
171
+ if (file.path.endsWith(".css")) {
172
+ resultHtml += (0, import_hexo_util.htmlTag)("link", { rel: "stylesheet", href: import_hexo_util.url_for.call(this, `${statics}${css}/${file}`), media: "none", onload: "this.media='all'" }, "");
173
+ }
174
+ });
175
+ return resultHtml;
176
+ });
156
177
  if (theme.experiments.cloudflarePatch) {
157
- await (0, import_esbuild.build)({
178
+ const resultCF = await (0, import_esbuild.build)({
179
+ write: false,
158
180
  entryPoints: [patchDir],
159
181
  bundle: true,
160
182
  platform: "browser",
@@ -174,7 +196,7 @@ hexo.extend.generator.register("script", async function(locals) {
174
196
  });
175
197
  res.push({
176
198
  path: theme.js + "/cf-patch.js",
177
- data: await import_promises.default.readFile("cf-patch.js")
199
+ data: resultCF.outputFiles[0].contents
178
200
  });
179
201
  }
180
202
  return res;
@@ -90,6 +90,7 @@ class SummaryDatabase {
90
90
  if (this.data.summaries[pathHash]?.sha256 === contentHash) {
91
91
  return this.data.summaries[pathHash].summary;
92
92
  } else {
93
+ hexo.log.info(`[ShokaX Summary AI] \u6B63\u5728\u5411 API \u8BF7\u6C42 ${path} \u7684\u6458\u8981`);
93
94
  const summaryContent = await getSummaryByAPI(content);
94
95
  this.data.summaries[pathHash] = {
95
96
  summary: summaryContent,
@@ -1,28 +1,5 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __copyProps = (to, from, except, desc) => {
9
- if (from && typeof from === "object" || typeof from === "function") {
10
- for (let key of __getOwnPropNames(from))
11
- if (!__hasOwnProp.call(to, key) && key !== except)
12
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
- }
14
- return to;
15
- };
16
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
- // If the importer is in node compatibility mode or this is not an ESM
18
- // file that has been converted to a CommonJS file using a Babel-
19
- // compatible transform (i.e. "__esModule" has not been set), then set
20
- // "default" to the CommonJS "module.exports" for node compatibility.
21
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
- mod
23
- ));
24
2
  var import_hexo_util = require("hexo-util");
25
- var import_node_fs = __toESM(require("node:fs"));
26
3
  const randomBG = function(count = 1, image_server = null, image_list = []) {
27
4
  let i;
28
5
  if (image_server) {
@@ -65,25 +42,8 @@ const randomBG = function(count = 1, image_server = null, image_list = []) {
65
42
  }
66
43
  return parseImage(image_list[Math.floor(Math.random() * image_list.length)], "mw690");
67
44
  };
68
- hexo.extend.helper.register("preloadjs", function() {
69
- const { statics, js } = hexo.theme.config;
70
- let res = "";
71
- import_node_fs.default.readdirSync("./shokaxTemp").forEach((file) => {
72
- if (file.endsWith(".js")) {
73
- res += (0, import_hexo_util.htmlTag)("link", { rel: "modulepreload", href: import_hexo_util.url_for.call(this, `${statics}${js}/${file}`) }, "");
74
- }
75
- });
76
- return res;
77
- });
78
- hexo.extend.helper.register("load_async_css", function() {
79
- const { statics, css } = hexo.theme.config;
80
- let res = "";
81
- import_node_fs.default.readdirSync("./shokaxTemp").forEach((file) => {
82
- if (file.endsWith(".css")) {
83
- res += (0, import_hexo_util.htmlTag)("link", { rel: "stylesheet", href: import_hexo_util.url_for.call(this, `${statics}${css}/${file}`), media: "none", onload: "this.media='all'" }, "");
84
- }
85
- });
86
- return res;
45
+ hexo.extend.helper.register("shokax_inject", function(point) {
46
+ return hexo.theme.config.injects[point].map((item) => this.partial(item.layout, item.locals, item.options)).join("");
87
47
  });
88
48
  hexo.extend.helper.register("_url", function(path, text, options = {}) {
89
49
  if (!path) {
@@ -1,7 +1,30 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __copyProps = (to, from, except, desc) => {
8
+ if (from && typeof from === "object" || typeof from === "function") {
9
+ for (let key of __getOwnPropNames(from))
10
+ if (!__hasOwnProp.call(to, key) && key !== except)
11
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
12
+ }
13
+ return to;
14
+ };
15
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
16
+ // If the importer is in node compatibility mode or this is not an ESM
17
+ // file that has been converted to a CommonJS file using a Babel-
18
+ // compatible transform (i.e. "__esModule" has not been set), then set
19
+ // "default" to the CommonJS "module.exports" for node compatibility.
20
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
21
+ mod
22
+ ));
1
23
  var import_package = require("../../package.json");
2
24
  var import_promises = require("node:fs/promises");
25
+ var import_injects = __toESM(require("./lib/injects"));
3
26
  hexo.on("generateBefore", async () => {
4
- await (0, import_promises.rm)("./shokaxTemp", { force: true, recursive: true });
27
+ (0, import_injects.default)(hexo);
5
28
  try {
6
29
  await (0, import_promises.unlink)("cf-patch.js");
7
30
  } catch (e) {
@@ -0,0 +1,40 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var injects_point_exports = {};
19
+ __export(injects_point_exports, {
20
+ default: () => injects_point_default
21
+ });
22
+ module.exports = __toCommonJS(injects_point_exports);
23
+ var injects_point_default = {
24
+ views: [
25
+ "head",
26
+ "sidebar",
27
+ "rightNav",
28
+ "postMeta",
29
+ "postBodyEnd",
30
+ "footer",
31
+ "bodyEnd",
32
+ "comment",
33
+ "status"
34
+ ],
35
+ styles: [
36
+ "variable",
37
+ "mixin",
38
+ "style"
39
+ ]
40
+ };
@@ -0,0 +1,105 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var injects_exports = {};
29
+ __export(injects_exports, {
30
+ default: () => injects_default
31
+ });
32
+ module.exports = __toCommonJS(injects_exports);
33
+ var import_node_fs = __toESM(require("node:fs"));
34
+ var import_node_path = __toESM(require("node:path"));
35
+ var import_injects_point = __toESM(require("./injects-point"));
36
+ /*!
37
+ inject.js in next-theme/hexo-theme-next by next-theme
38
+ under GNU AFFERO GENERAL PUBLIC LICENSE v3.0 OR LATER
39
+ https://github.com/next-theme/hexo-theme-next/blob/master/LICENSE.md
40
+ */
41
+ const defaultExtname = ".pug";
42
+ class StylusInject {
43
+ files;
44
+ base_dir;
45
+ constructor(base_dir) {
46
+ this.base_dir = base_dir;
47
+ this.files = [];
48
+ }
49
+ push(file) {
50
+ this.files.push(import_node_path.default.resolve(this.base_dir, file));
51
+ }
52
+ }
53
+ class ViewInject {
54
+ base_dir;
55
+ raws;
56
+ constructor(base_dir) {
57
+ this.base_dir = base_dir;
58
+ this.raws = [];
59
+ }
60
+ raw(name, raw, ...args) {
61
+ if (import_node_path.default.extname(name) === "") {
62
+ name += defaultExtname;
63
+ }
64
+ this.raws.push({ name, raw, args });
65
+ }
66
+ file(name, file, ...args) {
67
+ if (import_node_path.default.extname(name) === "") {
68
+ name += import_node_path.default.extname(file);
69
+ }
70
+ this.raw(name, import_node_fs.default.readFileSync(import_node_path.default.resolve(this.base_dir, file), "utf8"), ...args);
71
+ }
72
+ }
73
+ function initInject(base_dir) {
74
+ const injects = {};
75
+ import_injects_point.default.styles.forEach((item) => {
76
+ injects[item] = new StylusInject(base_dir);
77
+ });
78
+ import_injects_point.default.views.forEach((item) => {
79
+ injects[item] = new ViewInject(base_dir);
80
+ });
81
+ return injects;
82
+ }
83
+ var injects_default = (hexo) => {
84
+ const injects = initInject(hexo.base_dir);
85
+ hexo.execFilterSync("theme_inject", injects);
86
+ hexo.theme.config.injects = {};
87
+ import_injects_point.default.styles.forEach((type) => {
88
+ hexo.theme.config.injects[type] = injects[type].files;
89
+ });
90
+ import_injects_point.default.views.forEach((type) => {
91
+ const configs = /* @__PURE__ */ Object.create(null);
92
+ hexo.theme.config.injects[type] = [];
93
+ injects[type].raws.forEach((injectObj, index) => {
94
+ const name = `inject/${type}/${injectObj.name}`;
95
+ hexo.theme.setView(name, injectObj.raw);
96
+ configs[name] = {
97
+ layout: name,
98
+ locals: injectObj.args[0],
99
+ options: injectObj.args[1],
100
+ order: injectObj.args[2] || index
101
+ };
102
+ });
103
+ hexo.theme.config.injects[type] = Object.values(configs).sort((x, y) => x.order - y.order);
104
+ });
105
+ };
@@ -10,5 +10,5 @@ export const initAudioPlayer = async function () {
10
10
  })
11
11
  })
12
12
  const { initPlayer } = await import('nyx-player')
13
- initPlayer("#player","#showBtn", urls, "#playBtn")
13
+ initPlayer("#player","#showBtn", urls, "#playBtn", "html[data-theme=&quot;dark&quot;]", "shokax")
14
14
  }