hexo-theme-solitude 3.0.20 → 4.0.0

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 (143) hide show
  1. package/.github/FUNDING.yml +3 -3
  2. package/README.md +11 -1
  3. package/README_en-US.md +11 -1
  4. package/_config.yml +16 -7
  5. package/languages/default.yml +56 -36
  6. package/languages/en.yml +90 -69
  7. package/languages/es.yml +94 -73
  8. package/languages/zh-CN.yml +56 -34
  9. package/languages/zh-TW.yml +80 -57
  10. package/layout/archive.pug +60 -14
  11. package/layout/category.pug +13 -11
  12. package/layout/includes/body/gadsense.pug +2 -2
  13. package/layout/includes/console.pug +71 -72
  14. package/layout/includes/footer.pug +38 -24
  15. package/layout/includes/head/config.pug +83 -39
  16. package/layout/includes/head/mode.pug +7 -7
  17. package/layout/includes/head/page_config.pug +20 -11
  18. package/layout/includes/inject/body.pug +18 -40
  19. package/layout/includes/keyboard.pug +3 -3
  20. package/layout/includes/loading.pug +1 -37
  21. package/layout/includes/mixins/articleSort.pug +3 -3
  22. package/layout/includes/mixins/common.pug +8 -4
  23. package/layout/includes/mixins/pace.pug +1 -1
  24. package/layout/includes/mixins/pagination.pug +3 -3
  25. package/layout/includes/nav.pug +5 -4
  26. package/layout/includes/page/about.pug +9 -8
  27. package/layout/includes/page/brevity.pug +2 -2
  28. package/layout/includes/page/links.pug +23 -11
  29. package/layout/includes/page/music.pug +0 -5
  30. package/layout/includes/rightmenu.pug +20 -20
  31. package/layout/includes/sidebar.pug +2 -2
  32. package/layout/includes/widgets/aside/asideInfoCard.pug +2 -2
  33. package/layout/includes/widgets/aside/asideTag.pug +2 -2
  34. package/layout/includes/widgets/home/bbTimeList.pug +3 -3
  35. package/layout/includes/widgets/home/categoryBar.pug +30 -15
  36. package/layout/includes/widgets/home/hometop.pug +113 -8
  37. package/layout/includes/widgets/home/postList.pug +1 -2
  38. package/layout/includes/widgets/home/topGroup.pug +3 -7
  39. package/layout/includes/widgets/nav/group.pug +4 -2
  40. package/layout/includes/widgets/nav/menu.pug +6 -4
  41. package/layout/includes/widgets/nav/right.pug +7 -6
  42. package/layout/includes/widgets/page/about/award.pug +67 -38
  43. package/layout/includes/widgets/page/about/hobbies.pug +12 -3
  44. package/layout/includes/widgets/page/banner.pug +1 -1
  45. package/layout/includes/widgets/page/kit/content.pug +2 -2
  46. package/layout/includes/widgets/page/links/banner.pug +30 -19
  47. package/layout/includes/widgets/page/message/artalk.pug +3 -3
  48. package/layout/includes/widgets/page/message/content.pug +3 -3
  49. package/layout/includes/widgets/page/message/twikoo.pug +3 -3
  50. package/layout/includes/widgets/page/message/valine.pug +4 -4
  51. package/layout/includes/widgets/page/message/waline.pug +3 -3
  52. package/layout/includes/widgets/page/recentcomment/artalk.pug +5 -9
  53. package/layout/includes/widgets/page/recentcomment/twikoo.pug +5 -9
  54. package/layout/includes/widgets/page/recentcomment/valine.pug +6 -10
  55. package/layout/includes/widgets/page/recentcomment/waline.pug +5 -9
  56. package/layout/includes/widgets/post/award.pug +1 -28
  57. package/layout/includes/widgets/post/copyright.pug +10 -10
  58. package/layout/includes/widgets/post/postInfo.pug +10 -11
  59. package/layout/includes/widgets/post/postMeta.pug +3 -2
  60. package/layout/includes/widgets/rightside/hide.pug +5 -5
  61. package/layout/includes/widgets/rightside/show.pug +7 -7
  62. package/layout/includes/widgets/third-party/comments/artalk.pug +9 -9
  63. package/layout/includes/widgets/third-party/comments/giscus.pug +2 -2
  64. package/layout/includes/widgets/third-party/comments/twikoo.pug +5 -5
  65. package/layout/includes/widgets/third-party/comments/valine.pug +6 -6
  66. package/layout/includes/widgets/third-party/comments/waline.pug +6 -6
  67. package/layout/includes/widgets/third-party/hot/index.pug +1 -1
  68. package/layout/includes/widgets/third-party/hot/twikoo.pug +1 -1
  69. package/layout/includes/widgets/third-party/music.pug +9 -5
  70. package/layout/includes/widgets/third-party/news-comment/artalk.pug +6 -6
  71. package/layout/includes/widgets/third-party/news-comment/twikoo.pug +7 -7
  72. package/layout/includes/widgets/third-party/news-comment/valine.pug +8 -8
  73. package/layout/includes/widgets/third-party/news-comment/waline.pug +7 -7
  74. package/layout/includes/widgets/third-party/pjax.pug +16 -10
  75. package/layout/includes/widgets/third-party/search/algolia-search.pug +2 -2
  76. package/layout/includes/widgets/third-party/search/docsearch.pug +3 -3
  77. package/layout/includes/widgets/third-party/search/index.pug +1 -3
  78. package/layout/includes/widgets/third-party/search/local-search.pug +9 -9
  79. package/layout/index.pug +10 -8
  80. package/package.json +8 -7
  81. package/plugins.yml +22 -21
  82. package/scripts/event/cdn.js +11 -6
  83. package/scripts/event/friend_links.js +30 -0
  84. package/scripts/event/merge_config.js +15 -7
  85. package/scripts/filter/comment.js +2 -2
  86. package/scripts/filter/default.js +1 -1
  87. package/scripts/helper/inject_head_js.js +79 -33
  88. package/scripts/helper/related_post.js +16 -21
  89. package/scripts/tags/chart.js +1 -1
  90. package/scripts/tags/typeit.js +1 -1
  91. package/source/css/_components/capsule.styl +259 -168
  92. package/source/css/_components/rightside.styl +98 -242
  93. package/source/css/_global/animation.styl +1 -10
  94. package/source/css/_global/index.styl +27 -1
  95. package/source/css/_layout/aside.styl +7 -1
  96. package/source/css/_layout/console.styl +144 -163
  97. package/source/css/_layout/header.styl +238 -47
  98. package/source/css/_layout/pagination.styl +176 -132
  99. package/source/css/_layout/rightmenu.styl +11 -2
  100. package/source/css/_layout/sidebar.styl +39 -17
  101. package/source/css/_mode/index.styl +7 -1
  102. package/source/css/_page/_about/about.styl +18 -2
  103. package/source/css/_page/_about/buff.styl +1 -1
  104. package/source/css/_page/_about/contentinfo.styl +2 -1
  105. package/source/css/_page/_about/game.styl +57 -1
  106. package/source/css/_page/_about/like.styl +7 -2
  107. package/source/css/_page/_about/maxim.styl +1 -1
  108. package/source/css/_page/_about/reward.styl +226 -108
  109. package/source/css/_page/_about/skills.styl +5 -3
  110. package/source/css/_page/_home/category-bar.styl +162 -88
  111. package/source/css/_page/_home/home-top.styl +309 -263
  112. package/source/css/_page/_home/home.styl +34 -7
  113. package/source/css/_page/archive.styl +412 -0
  114. package/source/css/_page/category.styl +27 -1
  115. package/source/css/_page/index.styl +2 -0
  116. package/source/css/_page/links.styl +190 -0
  117. package/source/css/_page/other.styl +37 -0
  118. package/source/css/_post/index.styl +2 -1
  119. package/source/css/_post/meta.styl +245 -217
  120. package/source/css/_search/local-search.styl +158 -82
  121. package/source/css/var.styl +2 -2
  122. package/source/js/archive-page.js +191 -0
  123. package/source/js/core/actions.js +48 -0
  124. package/source/js/core/api.js +32 -0
  125. package/source/js/core/config.js +13 -0
  126. package/source/js/core/lifecycle.js +52 -0
  127. package/source/js/core/preloader.js +34 -0
  128. package/source/js/core/resources.js +65 -0
  129. package/source/js/covercolor/api.js +13 -103
  130. package/source/js/covercolor/ave.js +14 -77
  131. package/source/js/covercolor/local.js +22 -116
  132. package/source/js/covercolor/shared.js +102 -0
  133. package/source/js/friend_links.js +502 -0
  134. package/source/js/main.js +793 -177
  135. package/source/js/music.js +24 -14
  136. package/source/js/post_ai.js +13 -2
  137. package/source/js/right_menu.js +44 -24
  138. package/source/js/search/algolia.js +29 -21
  139. package/source/js/search/local.js +231 -266
  140. package/source/js/tw_cn.js +37 -12
  141. package/source/js/utils.js +37 -25
  142. package/layout/includes/widgets/randomlink.pug +0 -43
  143. package/scripts/filter/randomPosts.js +0 -16
@@ -0,0 +1,34 @@
1
+ export const initPreloader = (api) => {
2
+ if (document.documentElement.dataset.solitudePreloader === "true") return;
3
+ document.documentElement.dataset.solitudePreloader = "true";
4
+
5
+ let loaded = false;
6
+ let fallbackTimer;
7
+ const loadingBox = () => document.getElementById("loading-box");
8
+
9
+ const sync = () => {
10
+ const body = document.getElementById("body");
11
+ if (body) body.classList.toggle("pace-done", loaded);
12
+ };
13
+ const end = () => {
14
+ if (loaded) return;
15
+ loadingBox()?.classList.add("loaded");
16
+ loaded = true;
17
+ clearTimeout(fallbackTimer);
18
+ sync();
19
+ };
20
+ const start = () => {
21
+ loadingBox()?.classList.remove("loaded");
22
+ loaded = false;
23
+ clearTimeout(fallbackTimer);
24
+ fallbackTimer = setTimeout(end, 5000);
25
+ sync();
26
+ };
27
+
28
+ api.endLoading = end;
29
+ window.addEventListener("load", end, { once: true });
30
+ window.addEventListener("pjax:send", start);
31
+ document.addEventListener("pjax:complete", end);
32
+ fallbackTimer = setTimeout(end, 5000);
33
+ if (document.readyState === "complete") end();
34
+ };
@@ -0,0 +1,65 @@
1
+ const scriptRequests = new Map();
2
+ const styleRequests = new Map();
3
+
4
+ const loadElement = (cache, selector, create, url) => {
5
+ const absoluteUrl = new URL(url, document.baseURI).href;
6
+ if (cache.has(absoluteUrl)) return cache.get(absoluteUrl);
7
+
8
+ const existing = [...document.querySelectorAll(selector)].find(
9
+ (element) => element.href === absoluteUrl || element.src === absoluteUrl
10
+ );
11
+ if (existing) {
12
+ const resolved = Promise.resolve(existing);
13
+ cache.set(absoluteUrl, resolved);
14
+ return resolved;
15
+ }
16
+
17
+ const request = new Promise((resolve, reject) => {
18
+ const element = existing || create(absoluteUrl);
19
+ const complete = () => {
20
+ element.dataset.loaded = "true";
21
+ resolve(element);
22
+ };
23
+ const fail = () => {
24
+ cache.delete(absoluteUrl);
25
+ reject(new Error(`Unable to load ${absoluteUrl}`));
26
+ };
27
+
28
+ element.addEventListener("load", complete, { once: true });
29
+ element.addEventListener("error", fail, { once: true });
30
+ if (!existing) document.head.appendChild(element);
31
+ });
32
+
33
+ cache.set(absoluteUrl, request);
34
+ return request;
35
+ };
36
+
37
+ export const loadScript = (url, options = {}) =>
38
+ loadElement(
39
+ scriptRequests,
40
+ "script",
41
+ (src) => {
42
+ const script = document.createElement("script");
43
+ script.src = src;
44
+ script.async = options.async ?? true;
45
+ Object.entries(options.attributes || {}).forEach(([key, value]) =>
46
+ script.setAttribute(key, value)
47
+ );
48
+ return script;
49
+ },
50
+ url
51
+ );
52
+
53
+ export const loadStyle = (url, options = {}) =>
54
+ loadElement(
55
+ styleRequests,
56
+ "link",
57
+ (href) => {
58
+ const link = document.createElement("link");
59
+ link.rel = "stylesheet";
60
+ link.href = href;
61
+ if (options.id) link.id = options.id;
62
+ return link;
63
+ },
64
+ url
65
+ );
@@ -1,103 +1,13 @@
1
- const coverColor = (music = false) => {
2
- if (music) {
3
- var coverPath = document.querySelector("#nav-music .aplayer-pic").style.backgroundImage;
4
- const coverPathMatch = /url\("([^"]+)"\)/.exec(coverPath);
5
- coverPath = coverPathMatch ? coverPathMatch[1] : '';
6
- if (coverPath) {
7
- handleApiColor(coverPath,music);
8
- }
9
- }
10
- else {
11
- const pageColor = PAGE_CONFIG.color;
12
- if (pageColor) {
13
- setThemeColors(pageColor);
14
- return;
15
- }
16
-
17
- const path = document.getElementById("post-cover")?.src;
18
- if (path) {
19
- handleApiColor(path);
20
- } else {
21
- setDefaultThemeColors();
22
- }
23
- }
24
- }
25
-
26
- function handleApiColor(path, music = false) {
27
- const cacheGroup = JSON.parse(localStorage.getItem('Solitude')) || {};
28
- if (cacheGroup.postcolor?.[path]) {
29
- setThemeColors(cacheGroup.postcolor[path].value);
30
- } else {
31
- img2color(path);
32
- }
33
- if (music) {
34
- img2color(path, music);
35
- }
36
- }
37
-
38
- function setMusicColor(value) {
39
- if (!value) return setDefaultThemeColors();
40
- const item = document.querySelector("#nav-music")
41
- item.style.setProperty('--efu-music', value);
42
- }
43
-
44
- function img2color(src, music = false) {
45
- fetch(`${coverColorConfig.api}${encodeURIComponent(src)}`)
46
- .then(response => {
47
- if (!response.ok) throw new Error('Network response was not ok');
48
- return response.json();
49
- })
50
- .then(data => {
51
- if(music) {
52
- setMusicColor(data.RGB);
53
- }
54
- else {
55
- setThemeColors(data.RGB);
56
- cacheColor(src, data.RGB);
57
- }
58
- })
59
- .catch(error => console.error('Error fetching color:', error));
60
- }
61
-
62
- function setThemeColors(value) {
63
- if (value) {
64
- const [r, g, b] = value.match(/\w\w/g).map(x => parseInt(x, 16));
65
- document.documentElement.style.setProperty('--efu-main', value);
66
- document.documentElement.style.setProperty('--efu-main-op', `${value}23`);
67
- document.documentElement.style.setProperty('--efu-main-op-deep', `${value}dd`);
68
- document.documentElement.style.setProperty('--efu-main-none', `${value}00`);
69
- adjustBrightness(r, g, b);
70
- document.getElementById("coverdiv").classList.add("loaded");
71
- initThemeColor();
72
- } else {
73
- setDefaultThemeColors();
74
- }
75
- }
76
-
77
- function setDefaultThemeColors() {
78
- document.documentElement.style.setProperty('--efu-main', 'var(--efu-theme)');
79
- document.documentElement.style.setProperty('--efu-main-op', 'var(--efu-theme-op)');
80
- document.documentElement.style.setProperty('--efu-main-op-deep', 'var(--efu-theme-op-deep)');
81
- document.documentElement.style.setProperty('--efu-main-none', 'var(--efu-theme-none)');
82
- initThemeColor();
83
- }
84
-
85
- function cacheColor(src, color) {
86
- const cacheGroup = JSON.parse(localStorage.getItem('Solitude')) || {};
87
- cacheGroup.postcolor = cacheGroup.postcolor || {};
88
- cacheGroup.postcolor[src] = { value: color, expiration: Date.now() + coverColorConfig.time };
89
- localStorage.setItem('Solitude', JSON.stringify(cacheGroup));
90
- }
91
-
92
- function adjustBrightness(r, g, b) {
93
- const brightness = Math.round(((r * 299) + (g * 587) + (b * 114)) / 1000);
94
- if (brightness < 125) {
95
- [...document.getElementsByClassName('card-content')].forEach(item => {
96
- item.style.setProperty('--efu-card-bg', 'var(--efu-white)');
97
- });
98
- [...document.getElementsByClassName('sayhi')].forEach(item => {
99
- item.style.setProperty('background', 'var(--efu-white-op)');
100
- item.style.setProperty('color', 'var(--efu-white)');
101
- });
102
- }
103
- }
1
+ import { applyThemeColor, getCoverSource, resolveColor } from "./shared.js";
2
+
3
+ const fetchApiColor = async (source) => {
4
+ const response = await fetch(`${Solitude.config.covercolor.api}${encodeURIComponent(source)}`);
5
+ if (!response.ok) throw new Error(`Cover color API returned ${response.status}`);
6
+ return (await response.json()).RGB;
7
+ };
8
+
9
+ export const coverColor = (music = false) => {
10
+ const configured = !music && Solitude.page.color;
11
+ if (configured) return applyThemeColor(configured);
12
+ return resolveColor(getCoverSource(music), fetchApiColor, music);
13
+ };
@@ -1,77 +1,14 @@
1
- const coverColor = () => {
2
- const pageColor = PAGE_CONFIG.color;
3
- if (pageColor) {
4
- setThemeColors(pageColor);
5
- } else {
6
- const path = document.getElementById("post-cover")?.src;
7
- path ? handleApiColor(path) : setDefaultThemeColors();
8
- }
9
- }
10
-
11
- const handleApiColor = (path) => {
12
- const cacheGroup = JSON.parse(localStorage.getItem('Solitude')) || { postcolor: {} };
13
- const color = cacheGroup.postcolor[path]?.value;
14
- if (color) {
15
- setThemeColors(color);
16
- } else {
17
- img2color(path);
18
- }
19
- }
20
-
21
- const img2color = (src) => {
22
- fetch(`${src}?imageAve`)
23
- .then(response => {
24
- if (!response.ok) throw new Error('Network response was not ok');
25
- return response.json();
26
- })
27
- .then(({ RGB }) => {
28
- const formattedRGB = `#${RGB.slice(2)}`;
29
- setThemeColors(formattedRGB);
30
- cacheColor(src, formattedRGB);
31
- })
32
- .catch(error => console.error('Error fetching color:', error));
33
- }
34
-
35
- const setThemeColors = (value) => {
36
- if (!value) return setDefaultThemeColors();
37
- const [r, g, b] = value.match(/\w\w/g).map(x => parseInt(x, 16));
38
- const themeColors = {
39
- main: value,
40
- op: `${value}23`,
41
- opDeep: `${value}dd`,
42
- none: `${value}00`
43
- };
44
- Object.entries(themeColors).forEach(([key, color]) => {
45
- document.documentElement.style.setProperty(`--efu-${key}`, color);
46
- });
47
- adjustBrightness(r, g, b);
48
- document.getElementById("coverdiv").classList.add("loaded");
49
- initThemeColor();
50
- }
51
-
52
- const setDefaultThemeColors = () => {
53
- const vars = ['--efu-theme', '--efu-theme-op', '--efu-theme-op-deep', '--efu-theme-none'];
54
- vars.forEach((varName, i) => {
55
- document.documentElement.style.setProperty(['--efu-main', '--efu-main-op', '--efu-main-op-deep', '--efu-main-none'][i], `var(${varName})`);
56
- });
57
- initThemeColor();
58
- }
59
-
60
- const cacheColor = (src, color) => {
61
- const cacheGroup = JSON.parse(localStorage.getItem('Solitude')) || { postcolor: {} };
62
- cacheGroup.postcolor[src] = { value: color, expiration: Date.now() + coverColorConfig.time };
63
- localStorage.setItem('Solitude', JSON.stringify(cacheGroup));
64
- }
65
-
66
- const adjustBrightness = (r, g, b) => {
67
- const brightness = Math.round(((r * 299) + (g * 587) + (b * 114)) / 1000);
68
- if (brightness < 125) {
69
- document.querySelectorAll('.card-content').forEach(item => {
70
- item.style.setProperty('--efu-card-bg', 'var(--efu-white)');
71
- });
72
- document.querySelectorAll('.sayhi').forEach(item => {
73
- item.style.setProperty('background', 'var(--efu-white-op)');
74
- item.style.setProperty('color', 'var(--efu-white)');
75
- });
76
- }
77
- }
1
+ import { applyThemeColor, getCoverSource, resolveColor } from "./shared.js";
2
+
3
+ const fetchAveColor = async (source) => {
4
+ const response = await fetch(`${source}?imageAve`);
5
+ if (!response.ok) throw new Error(`Image average color returned ${response.status}`);
6
+ const { RGB } = await response.json();
7
+ return RGB ? `#${RGB.replace(/^0x/, "")}` : null;
8
+ };
9
+
10
+ export const coverColor = (music = false) => {
11
+ const configured = !music && Solitude.page.color;
12
+ if (configured) return applyThemeColor(configured);
13
+ return resolveColor(getCoverSource(music), fetchAveColor, music);
14
+ };
@@ -1,117 +1,23 @@
1
- const coverColor = (music = false) => {
2
- if (music) {
3
- var coverPath = document.querySelector("#nav-music .aplayer-pic").style.backgroundImage;
4
- const coverPathMatch = /url\("([^"]+)"\)/.exec(coverPath);
5
- coverPath = coverPathMatch ? coverPathMatch[1] : '';
6
- if (coverPath) {
7
- localColor(coverPath,music);
8
- }
1
+ import { applyThemeColor, getCoverSource, resolveColor, rgbToHex } from "./shared.js";
2
+
3
+ const extractLocalColor = (source) => new Promise((resolve, reject) => {
4
+ const image = new Image();
5
+ image.crossOrigin = "anonymous";
6
+ image.addEventListener("load", () => {
7
+ try {
8
+ const color = window.ColorThief?.getColorSync(image);
9
+ if (!color) throw new Error("Color Thief is unavailable");
10
+ resolve(rgbToHex(color.array(), 0.8));
11
+ } catch (error) {
12
+ reject(error);
9
13
  }
10
- else {
11
- const pageColor = PAGE_CONFIG.color || document.getElementById("post-cover")?.src;
12
- if (pageColor) {
13
- localColor(pageColor);
14
- } else {
15
- setDefaultThemeColors();
16
- }
17
- }
18
- }
19
-
20
- const setDefaultThemeColors = () => {
21
- const themeVars = {
22
- '--efu-main': 'var(--efu-theme)',
23
- '--efu-main-op': 'var(--efu-theme-op)',
24
- '--efu-main-op-deep': 'var(--efu-theme-op-deep)',
25
- '--efu-main-none': 'var(--efu-theme-none)'
26
- };
27
- Object.entries(themeVars).forEach(([key, value]) => {
28
- document.documentElement.style.setProperty(key, value);
29
- });
30
- initThemeColor();
31
- }
32
-
33
- const localColor = (path, music = false) => {
34
- const colorThief = new ColorThief();
35
- const img = new Image();
36
- img.crossOrigin = "Anonymous";
37
- img.onload = () => {
38
- const color = colorThief.getColor(img);
39
- if(music) {
40
- setMusicColor(rgbToHex(color));
41
- }
42
- else{
43
- setThemeColors(rgbToHex(color), ...color);
44
- }
45
- };
46
- img.onerror = () => console.error('Image Error');
47
- img.src = path;
48
- }
49
-
50
- const rgbToHex = ([r, g, b]) => {
51
- return '#' + [r, g, b].map(x => Math.floor(x * 0.8).toString(16).padStart(2, '0')).join('');
52
- }
53
-
54
- const setMusicColor = (value) => {
55
- if (!value) return setDefaultThemeColors();
56
- const item = document.querySelector("#nav-music")
57
- item.style.setProperty('--efu-music', value);
58
- }
59
-
60
-
61
- const setThemeColors = (value, r = null, g = null, b = null) => {
62
- if (!value) return setDefaultThemeColors();
63
-
64
- const themeColors = {
65
- '--efu-main': value,
66
- '--efu-main-op': value + '23',
67
- '--efu-main-op-deep': value + 'dd',
68
- '--efu-main-none': value + '00'
69
- };
70
-
71
- Object.entries(themeColors).forEach(([key, color]) => {
72
- document.documentElement.style.setProperty(key, color);
73
- });
74
-
75
- if (r !== null && g !== null && b !== null) {
76
- const brightness = Math.round(((r * 299) + (g * 587) + (b * 114)) / 1000);
77
- if (brightness < 125) {
78
- adjustCardStyles();
79
- value = LightenDarkenColor(value, 50);
80
- setThemeColors(value);
81
- }
82
- }
83
-
84
- document.getElementById("coverdiv").classList.add("loaded");
85
- initThemeColor();
86
- }
87
-
88
- const LightenDarkenColor = (col, amt) => {
89
- let usePound = false;
90
- if (col[0] === "#") {
91
- col = col.slice(1);
92
- usePound = true;
93
- }
94
- let num = parseInt(col, 16);
95
- let r = (num >> 16) + amt;
96
- let g = ((num >> 8) & 0x00FF) + amt;
97
- let b = (num & 0x0000FF) + amt;
98
-
99
- r = Math.max(Math.min(r, 255), 0);
100
- g = Math.max(Math.min(g, 255), 0);
101
- b = Math.max(Math.min(b, 255), 0);
102
-
103
- return (usePound ? "#" : "") + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
104
- }
105
-
106
- const adjustCardStyles = () => {
107
- const cardContents = document.querySelectorAll('.card-content');
108
- cardContents.forEach(item => {
109
- item.style.setProperty('--efu-card-bg', 'var(--efu-white)');
110
- });
111
-
112
- const authorInfo = document.querySelectorAll('.sayhi');
113
- authorInfo.forEach(item => {
114
- item.style.setProperty('background', 'var(--efu-white-op)');
115
- item.style.setProperty('color', 'var(--efu-white)');
116
- });
117
- }
14
+ }, { once: true });
15
+ image.addEventListener("error", () => reject(new Error(`Unable to load ${source}`)), { once: true });
16
+ image.src = source;
17
+ });
18
+
19
+ export const coverColor = (music = false) => {
20
+ const configured = !music && Solitude.page.color;
21
+ if (configured && /^#[0-9a-f]{6}$/i.test(configured)) return applyThemeColor(configured);
22
+ return resolveColor(getCoverSource(music), extractLocalColor, music);
23
+ };
@@ -0,0 +1,102 @@
1
+ import { Solitude } from "../core/api.js";
2
+
3
+ const STORAGE_KEY = "Solitude";
4
+
5
+ const readStore = () => {
6
+ try {
7
+ return JSON.parse(localStorage.getItem(STORAGE_KEY)) || {};
8
+ } catch (error) {
9
+ return {};
10
+ }
11
+ };
12
+
13
+ const writeStore = (value) => {
14
+ try {
15
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
16
+ } catch (error) {
17
+ // Theme colors remain usable when storage is unavailable.
18
+ }
19
+ };
20
+
21
+ export const getCachedColor = (source) => {
22
+ const item = readStore().postcolor?.[source];
23
+ if (!item || (item.expiration && item.expiration <= Date.now())) return null;
24
+ return item.value;
25
+ };
26
+
27
+ export const cacheColor = (source, color) => {
28
+ const store = readStore();
29
+ store.postcolor ||= {};
30
+ store.postcolor[source] = {
31
+ value: color,
32
+ expiration: Date.now() + (Number(Solitude.config.covercolor.time) || 43200000),
33
+ };
34
+ writeStore(store);
35
+ };
36
+
37
+ export const getCoverSource = (music = false) => {
38
+ if (!music) {
39
+ return Solitude.page.color || document.getElementById("post-cover")?.src || "";
40
+ }
41
+ const background = document.querySelector("#nav-music .aplayer-pic")?.style.backgroundImage || "";
42
+ return /url\(["']?([^"')]+)["']?\)/.exec(background)?.[1] || "";
43
+ };
44
+
45
+ export const rgbToHex = ([r, g, b], factor = 1) =>
46
+ `#${[r, g, b]
47
+ .map((value) => Math.max(0, Math.min(255, Math.floor(value * factor))).toString(16).padStart(2, "0"))
48
+ .join("")}`;
49
+
50
+ const normalizeHex = (value) => {
51
+ if (!value) return null;
52
+ const hex = value.startsWith("#") ? value : `#${value}`;
53
+ return /^#[0-9a-f]{6}$/i.test(hex) ? hex : null;
54
+ };
55
+
56
+ export const applyMusicColor = (value) => {
57
+ const color = normalizeHex(value);
58
+ if (color) document.getElementById("nav-music")?.style.setProperty("--efu-music", color);
59
+ };
60
+
61
+ export const applyThemeColor = (value) => {
62
+ const color = normalizeHex(value);
63
+ if (!color) return applyDefaultColor();
64
+ const [r, g, b] = color.match(/[0-9a-f]{2}/gi).map((part) => parseInt(part, 16));
65
+ const brightness = Math.round((r * 299 + g * 587 + b * 114) / 1000);
66
+ const adjusted = brightness < 125
67
+ ? rgbToHex([Math.min(r + 50, 255), Math.min(g + 50, 255), Math.min(b + 50, 255)])
68
+ : color;
69
+
70
+ const root = document.documentElement;
71
+ root.style.setProperty("--efu-main", adjusted);
72
+ root.style.setProperty("--efu-main-op", `${adjusted}23`);
73
+ root.style.setProperty("--efu-main-op-deep", `${adjusted}dd`);
74
+ root.style.setProperty("--efu-main-none", `${adjusted}00`);
75
+ Solitude.initThemeColor?.();
76
+ };
77
+
78
+ export const applyDefaultColor = () => {
79
+ const root = document.documentElement;
80
+ root.style.setProperty("--efu-main", "var(--efu-theme)");
81
+ root.style.setProperty("--efu-main-op", "var(--efu-theme-op)");
82
+ root.style.setProperty("--efu-main-op-deep", "var(--efu-theme-op-deep)");
83
+ root.style.setProperty("--efu-main-none", "var(--efu-theme-none)");
84
+ Solitude.initThemeColor?.();
85
+ };
86
+
87
+ export const resolveColor = async (source, fetchColor, music = false) => {
88
+ if (!source) return applyDefaultColor();
89
+ const cached = getCachedColor(source);
90
+ if (cached) return music ? applyMusicColor(cached) : applyThemeColor(cached);
91
+
92
+ try {
93
+ const color = await fetchColor(source);
94
+ if (!color) throw new Error("Color provider returned no color");
95
+ cacheColor(source, color);
96
+ if (source !== getCoverSource(music)) return;
97
+ return music ? applyMusicColor(color) : applyThemeColor(color);
98
+ } catch (error) {
99
+ console.error("Unable to resolve cover color:", error);
100
+ if (!music) applyDefaultColor();
101
+ }
102
+ };