hexo-theme-solitude 3.0.10 → 3.0.11

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 (34) hide show
  1. package/_config.yml +15 -27
  2. package/languages/default.yml +14 -15
  3. package/languages/en.yml +22 -24
  4. package/languages/zh-CN.yml +14 -15
  5. package/languages/zh-TW.yml +14 -15
  6. package/layout/includes/head/config.pug +0 -11
  7. package/layout/includes/head/page_config.pug +1 -1
  8. package/layout/includes/inject/body.pug +1 -1
  9. package/layout/includes/layout.pug +0 -4
  10. package/layout/includes/page/brevity.pug +2 -2
  11. package/layout/includes/widgets/page/about/skillsinfo.pug +12 -11
  12. package/layout/includes/widgets/page/kit/content.pug +3 -2
  13. package/layout/includes/widgets/page/links/banner.pug +7 -6
  14. package/layout/includes/widgets/post/ai.pug +7 -0
  15. package/layout/post.pug +2 -2
  16. package/package.json +1 -1
  17. package/scripts/event/cdn.js +83 -60
  18. package/scripts/event/merge_config.js +385 -385
  19. package/scripts/filter/default.js +22 -24
  20. package/scripts/tags/tabs.js +29 -18
  21. package/source/css/_global/index.styl +2 -2
  22. package/source/css/_layout/article-container.styl +1 -1
  23. package/source/css/_layout/other.styl +2 -2
  24. package/source/css/_post/index.styl +103 -4
  25. package/source/css/_tags/tabs.styl +40 -101
  26. package/source/css/index.styl +0 -4
  27. package/source/js/main.js +447 -257
  28. package/source/js/post_ai.js +60 -0
  29. package/source/js/utils.js +0 -6
  30. package/layout/includes/widgets/post/post-ai.pug +0 -12
  31. package/layout/includes/widgets/third-party/tianli-talk.pug +0 -26
  32. package/source/css/_post/postAI.styl +0 -132
  33. package/source/css/third_party/tianli_talk.styl +0 -77
  34. package/source/js/third_party/post_ai.min.js +0 -184
@@ -0,0 +1,60 @@
1
+ class AIPostRenderer {
2
+ static ANIMATION_DELAY_MS = 30;
3
+ static AI_EXPLANATION_SELECTOR = ".ai-explanation";
4
+ static AI_TAG_SELECTOR = ".ai-tag";
5
+
6
+ init() {
7
+ this.tagElement = document.querySelector(AIPostRenderer.AI_TAG_SELECTOR);
8
+ this.isAnimating = false;
9
+ this.aiContent = PAGE_CONFIG?.ai_text || "";
10
+ this.explanationElement = document.querySelector(
11
+ AIPostRenderer.AI_EXPLANATION_SELECTOR
12
+ );
13
+ this.renderAIContent();
14
+ }
15
+
16
+ renderAIContent() {
17
+ if (!this.validateElements() || !this.aiContent) return;
18
+
19
+ this.prepareAnimation();
20
+ this.startTextAnimation();
21
+ }
22
+
23
+ validateElements() {
24
+ return this.explanationElement && this.tagElement && !this.isAnimating;
25
+ }
26
+
27
+ prepareAnimation() {
28
+ this.isAnimating = true;
29
+ this.tagElement.classList.add("loadingAI");
30
+ this.explanationElement.innerHTML = "";
31
+ }
32
+
33
+ startTextAnimation() {
34
+ const animate = (index) => {
35
+ if (index >= this.aiContent.length) {
36
+ this.completeAnimation();
37
+ return;
38
+ }
39
+
40
+ this.appendCharacter(this.aiContent[index]);
41
+ setTimeout(() => animate(index + 1), AIPostRenderer.ANIMATION_DELAY_MS);
42
+ };
43
+
44
+ animate(0);
45
+ }
46
+
47
+ appendCharacter(char) {
48
+ const charElement = document.createElement("span");
49
+ charElement.className = "char";
50
+ charElement.textContent = char;
51
+ this.explanationElement.appendChild(charElement);
52
+ }
53
+
54
+ completeAnimation() {
55
+ this.isAnimating = false;
56
+ this.tagElement.classList.remove("loadingAI");
57
+ }
58
+ }
59
+
60
+ const ai = new AIPostRenderer();
@@ -31,12 +31,6 @@
31
31
  ele.addEventListener('animationend', resetStyles);
32
32
  ele.style.animation = `to_hide ${time}s`;
33
33
  },
34
- sidebarPaddingR: () => {
35
- const paddingRight = window.innerWidth - document.body.clientWidth;
36
- if (paddingRight > 0) {
37
- document.body.style.paddingRight = `${paddingRight}px`;
38
- }
39
- },
40
34
  snackbarShow: (text, showAction = false, duration = 5000) => {
41
35
  Snackbar.show({ text, showAction, duration, pos: 'top-center' });
42
36
  },
@@ -1,12 +0,0 @@
1
- .post-ai
2
- .ai-title
3
- .ai-title-left
4
- i.ai-title-icon.solitude.fab.fa-bilibili
5
- .ai-title-text=_p('post.ai.title')
6
- .ai-tag#ai-tag= theme.post_ai.modelName
7
- .ai-explanation(style="display: block;")
8
- .ai-suggestions
9
- .ai-bottom
10
- .ai-tips= theme.post_ai.tips
11
- if theme.post_ai.report
12
- a.ai-report(title=_p('post.ai.tip'), href=url_for(theme.post_ai.report))=_p('post.ai.tip')
@@ -1,26 +0,0 @@
1
- - const {key,option,title} = theme.tianli_talk
2
-
3
- span.needEndHide#efuTalk(onclick="togglePostChatContainer(); return false;" title=title)
4
- i.solitude.fab.fa-bilibili
5
- span.efuTalkTitle=title
6
-
7
- script.
8
- var postChatConfig = {
9
- backgroundColor: "var(--efu-main)",
10
- bottom: "60px",
11
- ...!{JSON.stringify(option)}
12
- };
13
-
14
- let b = true;
15
-
16
- document.getElementById('efuTalk').addEventListener('click', function () {
17
- this.classList.toggle('on');
18
- let efu = this
19
- b && setTimeout(() => {
20
- document.getElementById('close-button').addEventListener('click', function () {
21
- efu.classList.remove('on');
22
- })
23
- }, 0);
24
- b = false
25
- });
26
- script(pjax data-postChat_key=key src="https://ai.tianli0.top/static/public/postChatUser.min.js")
@@ -1,132 +0,0 @@
1
- .post-ai
2
- background var(--efu-secondbg)
3
- border-radius 12px
4
- padding 12px
5
- line-height 1.3
6
- border var(--style-border-always)
7
- margin-top 16px
8
- min-height 101.22px
9
- box-shadow var(--efu-shadow-border)
10
-
11
- +maxWidth768()
12
- margin-top 0
13
-
14
- .ai-title
15
- display flex
16
- color var(--efu-lighttext)
17
- border-radius 8px
18
- align-items center
19
- user-select none
20
-
21
- .ai-title-left
22
- display flex
23
- align-items center
24
- color var(--efu-lighttext)
25
-
26
- i.ai-title-icon
27
- width 24px
28
- height 24px
29
- display flex
30
- background var(--efu-lighttext)
31
- color var(--efu-card-bg)
32
- font-size 14px
33
- border-radius 20px
34
- justify-content center
35
- align-items center
36
-
37
- .ai-title-text
38
- font-weight 700
39
- margin-left 8px
40
- line-height 1
41
- font-size 14px
42
-
43
- .ai-tag
44
- font-size 12px
45
- background-color var(--efu-lighttext)
46
- box-shadow var(--efu-shadow-main)
47
- color var(--efu-card-bg)
48
- font-weight 700
49
- border-radius 12px
50
- margin-left auto
51
- line-height 12px
52
- padding 6px 8px
53
- display flex
54
- align-items center
55
- justify-content center
56
- cursor pointer
57
- transition .3s
58
-
59
- &.loadingAI
60
- animation-duration 2s
61
- animation-name AILoading
62
- animation-iteration-count infinite
63
- animation-direction alternate
64
- cursor default
65
-
66
- &:hover:not(.loadingAI)
67
- background var(--efu-fontcolor)
68
- color var(--efu-card-bg)
69
-
70
- .ai-explanation
71
- margin-top 12px
72
- padding 8px 12px
73
- background var(--efu-card-bg)
74
- border-radius 8px
75
- border var(--style-border-always)
76
- font-size 15px
77
- line-height 1.4
78
- display none
79
- text-align left
80
-
81
- .blinking-cursor
82
- background-color var(--efu-lighttext)
83
- width 14px
84
- height 14px
85
- border-radius 16px
86
- display inline-block
87
- vertical-align middle
88
- animation blinking-cursor 2s infinite
89
- margin-left 4px
90
- margin-bottom 3px
91
- transform scale(.6)
92
-
93
- .ai-suggestions
94
- display flex
95
- flex-wrap wrap
96
-
97
- .ai-suggestions-item
98
- margin-top 12px
99
- padding 8px 12px
100
- background var(--efu-card-bg)
101
- border-radius 8px 8px 8px 0
102
- border var(--style-border-always)
103
- font-size 14px
104
- line-height 1.4
105
- display flex
106
- width fit-content
107
- margin-right 12px
108
- cursor pointer
109
- transition .3s
110
-
111
- &:hover
112
- background var(--efu-main)
113
- color var(--efu-white)
114
-
115
- .ai-bottom
116
- width 100%
117
- margin-top 12px
118
- display flex
119
-
120
- .ai-tips
121
- font-size 12px
122
- color var(--efu-secondtext)
123
- margin-right auto
124
-
125
- .ai-report
126
- font-size 12px
127
- color var(--efu-secondtext) !important
128
- white-space nowrap
129
- border-bottom none!important
130
-
131
- &:hover
132
- background: none !important
@@ -1,77 +0,0 @@
1
- #efuTalk
2
- display flex
3
- align-items center
4
- z-index 1001
5
- position fixed
6
- bottom 70px
7
- left 20px
8
- height 38px
9
- cursor pointer
10
- transition .3s
11
- box-shadow var(--efu-shadow-border)
12
- background var(--efu-card-bg)
13
- border-radius 20px
14
- overflow hidden
15
- padding 10px
16
- color var(--efu-fontcolor)
17
- width 42px
18
- border var(--style-border-always)
19
-
20
- &:hover
21
- background var(--efu-lighttext)
22
- color var(--efu-card-bg)
23
- width 159px
24
- border-color var(--efu-lighttext)
25
-
26
- span.efuTalkTitle
27
- color var(--efu-card-bg)
28
- opacity 1
29
-
30
- i.solitude.efuTalkIcon
31
- width 18px
32
- height 18px
33
- font-size 18px
34
- margin-left 3px
35
- line-height 1
36
-
37
- span.efuTalkTitle
38
- display flex
39
- font-size 14px
40
- padding 0 12px
41
- line-height 1
42
- white-space nowrap
43
- opacity 0
44
- transition .3s
45
-
46
- #post_chat_button
47
- display none
48
-
49
- +maxWidth768()
50
- display block
51
-
52
- #postChat_iframeContainer
53
- z-index 1000
54
- border var(--style-border-always)
55
- background var(--efu-card-bg)
56
- animation to_show_fromLeftBottom .2s ease-out
57
- border-radius 12px
58
-
59
- +maxWidth768()
60
- width 100% !important
61
- height 100% !important
62
- bottom 0 !important
63
- left 0 !important
64
- max-width 100% !important
65
- max-height 100% !important
66
- border-radius 0 !important
67
-
68
- #postChat_button
69
- display none !important
70
-
71
- @keyframes to_show_fromLeftBottom
72
- from
73
- opacity 0
74
- transform scale(0)
75
- to
76
- opacity 1
77
- transform scale(1)
@@ -1,184 +0,0 @@
1
- class POST_AI {
2
- constructor() {
3
- this.root = "https://summary.tianli0.top";
4
- this.aiTalkMode = false;
5
- this.aiPostExplanation = '';
6
- this.config = GLOBAL_CONFIG.post_ai;
7
- this.scoGPTIsRunning = false;
8
- console.log('%c TianliGPT %c 文章摘要 %c https://postchat.zhheo.com',
9
- 'background:#35495e ; padding: 1px; border-radius: 3px 0 0 3px; color: #fff',
10
- 'background:#ff9a9a ; padding: 1px; border-radius: 0 3px 3px 0; color: #fff',
11
- 'background:unset ; padding: 1px; border-radius: 0 3px 3px 0; color: #fff');
12
- }
13
-
14
- init() {
15
- const explanationElement = document.querySelector(".ai-explanation");
16
- if (!explanationElement) return;
17
-
18
- this.scoGPTIsRunning = false;
19
- this.aiPostExplanation = PAGE_CONFIG.ai_text || '';
20
-
21
- if (!this.aiPostExplanation) {
22
- this.generate();
23
- } else {
24
- this.aiShowAnimation(Promise.resolve(this.aiPostExplanation));
25
- }
26
- this.AIEngine();
27
- }
28
-
29
- getTitleAndContent() {
30
- const articleContainer = document.querySelector(".article-container");
31
- const title = document.title;
32
- const paragraphs = articleContainer.getElementsByTagName("p");
33
- const headers = articleContainer.querySelectorAll("h1, h2, h3, h4, h5");
34
-
35
- return (title + " " + [...headers, ...paragraphs]
36
- .map(element => element.innerText.replace(/https?:\/\/[^\s]+/g, ""))
37
- .join(" ")).slice(0, 1000);
38
- }
39
-
40
- async generate() {
41
- const title = document.title;
42
- const content = this.getTitleAndContent();
43
- const key = this.config.key;
44
- this.aiShowAnimation(this.fetch(title, content, key));
45
- }
46
-
47
- async fetch(title, content, key) {
48
- const url = `${this.root}/?content=${encodeURIComponent(content)}&title=${title}&key=${encodeURIComponent(key)}&url=${encodeURIComponent(window.location.href)}`;
49
- try {
50
- const response = await fetch(url);
51
- const data = await response.json();
52
- if (response.ok) {
53
- this.aiPostExplanation = data.summary;
54
- return data.summary;
55
- } else {
56
- console.error("Request failed:", data.err_msg);
57
- return data.err_msg;
58
- }
59
- } catch (error) {
60
- console.error("Fetch error:", error);
61
- return "Error fetching data";
62
- }
63
- }
64
-
65
- aiShowAnimation(promise, onComplete = false, delay = 0) {
66
- const explanationElement = document.querySelector(".ai-explanation");
67
- const tagElement = document.querySelector(".ai-tag");
68
- if (!explanationElement || this.scoGPTIsRunning) return;
69
-
70
- this.scoGPTIsRunning = true;
71
- this.cleanSuggestions();
72
- tagElement.classList.add("loadingAI");
73
- explanationElement.style.display = "block";
74
- explanationElement.innerHTML = '生成中...<span class="blinking-cursor"></span>';
75
-
76
- setTimeout(() => {
77
- let startTime, update, currentIndex = 0, isIntersecting = true, isInitial = true;
78
- const observer = new IntersectionObserver(entries => {
79
- isIntersecting = entries[0].isIntersecting;
80
- if (isIntersecting) requestAnimationFrame(update);
81
- }, { threshold: 0 });
82
-
83
- promise.then(result => {
84
- startTime = performance.now();
85
- update = () => {
86
- if (currentIndex < result.length && isIntersecting) {
87
- const now = performance.now();
88
- const timeElapsed = now - startTime;
89
- const char = result.slice(currentIndex, currentIndex + 1);
90
- const isPunctuation = /[,。!、?,.!?]/.test(char);
91
- const isAlphaNumeric = /[a-zA-Z0-9]/.test(char);
92
- const delay = isPunctuation ? 100 * Math.random() + 100 : (isAlphaNumeric ? 10 : 25);
93
-
94
- if (timeElapsed >= delay) {
95
- explanationElement.innerText = result.slice(0, currentIndex + 1);
96
- startTime = now;
97
- currentIndex++;
98
- if (currentIndex < result.length) {
99
- explanationElement.innerHTML = result.slice(0, currentIndex) + '<span class="blinking-cursor"></span>';
100
- } else {
101
- explanationElement.innerHTML = result;
102
- explanationElement.style.display = "block";
103
- this.scoGPTIsRunning = false;
104
- tagElement.classList.remove("loadingAI");
105
- observer.disconnect();
106
- if (onComplete) this.createSuggestions();
107
- }
108
- }
109
- if (isIntersecting) requestAnimationFrame(update);
110
- }
111
- };
112
- if (isIntersecting && isInitial) {
113
- requestAnimationFrame(update);
114
- isInitial = false;
115
- }
116
- observer.observe(explanationElement);
117
- }).catch(error => {
118
- console.error("检索信息失败:", error);
119
- explanationElement.innerHTML = "检索信息失败";
120
- explanationElement.style.display = "block";
121
- this.scoGPTIsRunning = false;
122
- tagElement.classList.remove("loadingAI");
123
- observer.disconnect();
124
- });
125
- }, delay);
126
- }
127
-
128
- AIEngine() {
129
- const tagElement = document.querySelector(".ai-tag");
130
- if (tagElement) {
131
- tagElement.addEventListener("click", () => {
132
- if (!this.scoGPTIsRunning) {
133
- this.aiTalkMode = true;
134
- this.aiShowAnimation(Promise.resolve(this.config.talk), true);
135
- }
136
- });
137
- }
138
- }
139
-
140
- cleanSuggestions() {
141
- const suggestionsElement = document.querySelector(".ai-suggestions");
142
- if (suggestionsElement) {
143
- suggestionsElement.innerHTML = "";
144
- } else {
145
- console.error("没有这个元素:'ai-suggestions'");
146
- }
147
- }
148
-
149
- createSuggestions() {
150
- if (this.aiTalkMode) {
151
- this.cleanSuggestions();
152
- this.createSuggestionItemWithAction("这篇文章讲了什么?", () => {
153
- if (this.aiPostExplanation === "") {
154
- setTimeout(() => {
155
- this.generate();
156
- }, 3000);
157
- } else {
158
- setTimeout(() => {
159
- this.aiShowAnimation(Promise.resolve(this.aiPostExplanation), true);
160
- }, 3000);
161
- }
162
- });
163
- if (this.config.randomPost) {
164
- this.createSuggestionItemWithAction("带我去看看其他文章", () => toRandomPost());
165
- }
166
- this.aiTalkMode = true;
167
- }
168
- }
169
-
170
- createSuggestionItemWithAction(text, action) {
171
- const suggestions = document.querySelector(".ai-suggestions");
172
- if (!suggestions) {
173
- console.error("无法找到具有class为ai-suggestions的元素");
174
- return;
175
- }
176
- const item = document.createElement("div");
177
- item.classList.add("ai-suggestions-item");
178
- item.textContent = text;
179
- item.addEventListener("click", action);
180
- suggestions.appendChild(item);
181
- }
182
- }
183
-
184
- const ai = new POST_AI();