hexo-theme-stellar 1.30.4 → 1.31.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.
package/_config.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  ######## Stellar info ########
2
2
  stellar:
3
- version: '1.30.4'
3
+ version: '1.31.0'
4
4
  homepage: 'https://xaoxuu.com/wiki/stellar/'
5
5
  repo: 'https://github.com/xaoxuu/hexo-theme-stellar'
6
6
  main_css: /css/main.css
@@ -13,10 +13,20 @@ preconnect:
13
13
  # - https://gcore.jsdelivr.net
14
14
  # - https://unpkg.com
15
15
 
16
+ # 一旦设置源站地址,非源站地址将不会被SEO收录,并且访问时弹出提示
17
+ # 如果访问地址不在备用站主机列表,则警告信息为非法克隆
18
+ canonical:
19
+ originalHost: # 主站点域名主机,例如 xaoxuu.com
20
+ officialHosts: # 官方主机列表,每行一个,例如 xaoxuu.vercel.app
21
+ - localhost
22
+
16
23
  open_graph:
17
24
  enable: true
18
25
  twitter_id: # for open_graph meta
19
26
 
27
+ structured_data:
28
+ links: [] # your social links.
29
+
20
30
 
21
31
  ######## Sidebar ########
22
32
  # 左上角显示的 logo 区域,包含图标、大标题、副标题
@@ -439,7 +449,7 @@ data_services:
439
449
  siteinfo:
440
450
  js: /js/services/siteinfo.js
441
451
  # 设置 api 可以自动提取网页标题、图标,服务部署方法:https://github.com/xaoxuu/site-info-api/
442
- # 接口测试通过后,把按钮的 href 部分替换成 {href} 之后填写到下方,例如:https://api.vlts.cc/site_info/v1?url={href}
452
+ # 接口测试通过后,把按钮的 href 部分替换成 {href} 之后填写到下方,例如:https://api.xaox.cc/site_info/v1?url={href}
443
453
  api:
444
454
  ghinfo:
445
455
  js: /js/services/ghinfo.js
@@ -673,6 +683,8 @@ default:
673
683
 
674
684
 
675
685
  api_host:
676
- ghapi: https://api.github.com
677
- ghraw: https://raw.githubusercontent.com
678
- gist: https://gist.github.com
686
+ ghapi: api.github.com
687
+ ghraw: raw.githubusercontent.com
688
+ gist: gist.github.com
689
+ # https://github.com/anuraghazra/github-readme-stats
690
+ ghcard: github-readme-stats.vercel.app
package/_data/widgets.yml CHANGED
@@ -68,7 +68,6 @@ timeline:
68
68
  title: 近期动态
69
69
  api: # https://api.github.com/repos/xaoxuu/hexo-theme-stellar/issues
70
70
  user: # 默认显示所有人的数据,设置名称可过滤为仅显示某人的数据,多个名称用英文逗号隔开,不要加空格
71
- type: # 默认不用写,如果是友链朋友圈数据请写 fcircle
72
71
  limit: # 默认通过 api 上增加 per_page 来设置,如果是友链朋友圈,可通过这个设置数量
73
72
 
74
73
  latest_comment:
@@ -47,6 +47,10 @@ function generate_keywords() {
47
47
  }
48
48
 
49
49
  function generate_robots() {
50
+ const IS_BACKUP = process.env.IS_BACKUP === 'true';
51
+ if (IS_BACKUP) {
52
+ return '<meta name="robots" content="noindex, nofollow">';
53
+ }
50
54
  if (is_home() == true) {
51
55
  return '';
52
56
  }
@@ -62,6 +66,7 @@ function og_args() {
62
66
  if (page.layout == 'post' && page.cover) {
63
67
  args.twitter_card = 'summary_large_image';
64
68
  }
69
+ args.image = config.avatar || (config.email ? gravatar(config.email) : null);
65
70
  return Object.assign(args, page.open_graph);
66
71
  }
67
72
 
@@ -104,7 +109,9 @@ function custom_inject() {
104
109
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
105
110
  <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#000">
106
111
  <meta name="theme-color" content="#f9fafb">
107
-
112
+ <% if (theme.canonical.originalHost) { %>
113
+ <link rel="canonical" href="https://<%= theme.canonical.originalHost + "/" + page.path %>">
114
+ <% } %>
108
115
  <title><%- generate_title() %></title>
109
116
 
110
117
  <% if (theme.open_graph && theme.open_graph.enable) { %>
@@ -5,7 +5,7 @@ function layoutDiv() {
5
5
  return ''
6
6
  }
7
7
  return `
8
- <div class="right ghrepo ds-ghinfo" api="${theme.api_host.ghapi}/repos/${repo}">
8
+ <div class="right ghrepo ds-ghinfo" api="https://${theme.api_host.ghapi}/repos/${repo}">
9
9
  <a class="repo-link bold" href="https://github.com/${repo}">
10
10
  ${icon('github:repo')}
11
11
  <span type="text">${repo}</span>
@@ -1,4 +1,13 @@
1
+ <%
2
+ const canonical = theme.canonical || {}
3
+ const encoded = Buffer.from(canonical.originalHost || '').toString('base64');
4
+ const canonicalWithEncoded = Object.assign({}, canonical, {
5
+ encoded: encoded
6
+ });
7
+ %>
8
+
1
9
  <script type="text/javascript">
10
+ window.canonical = <%- JSON.stringify(canonicalWithEncoded) %>;
2
11
  const ctx = {
3
12
  date_suffix: {
4
13
  just: `<%- __('meta.date_suffix.just') %>`,
@@ -15,7 +15,7 @@ function layoutDiv() {
15
15
  if (repo == undefined) {
16
16
  return el;
17
17
  }
18
- item.api = theme.api_host.ghapi + '/repos/' + repo + '/issues?per_page=' + item.limit;
18
+ item.api = `https://${theme.api_host.ghapi}/repos/${repo}/issues?per_page=${item.limit}`;
19
19
  if (item.labels) {
20
20
  item.api += '&labels=' + item.labels;
21
21
  }
@@ -19,7 +19,7 @@ function layoutDiv() {
19
19
  el += `<widget class="widget-wrapper${scrollreveal(' ')} ghrepo">`
20
20
  // body
21
21
  el += '<div class="widget-body">';
22
- el += '<div class="items ds-ghinfo" api="' + theme.api_host.ghapi + '/repos/' + repo + '">';
22
+ el += '<div class="items ds-ghinfo" api="https://' + theme.api_host.ghapi + '/repos/' + repo + '">';
23
23
  el += '<a class="repo" href="https://github.com/' + repo + '" target="_blank" rel="external nofollow noopener noreferrer">';
24
24
  el += '<div class="repo-name flex-row">';
25
25
  el += icon('github:repo')
@@ -35,7 +35,7 @@ function layoutDiv() {
35
35
  el += icon('github:fork')
36
36
  el += '<span type="text" id="forks_count"></span>';
37
37
  el += '</div>';
38
- el += '<div class="flex-row ds-ghinfo" index="0" api="' + theme.api_host.ghapi + '/repos/' + repo + '/tags">';
38
+ el += '<div class="flex-row ds-ghinfo" index="0" api="https://' + theme.api_host.ghapi + '/repos/' + repo + '/tags">';
39
39
  el += icon('github:tag')
40
40
  el += '<span type="text" id="latest-tag-name">0</span>';
41
41
  el += '</div>';
@@ -24,7 +24,7 @@ function layoutDiv() {
24
24
 
25
25
  el += `<widget class="widget-wrapper${scrollreveal(' ')} user-card ghuser">`
26
26
  // body
27
- el += '<div class="widget-body ds-ghinfo" api="' + theme.api_host.ghapi + '/users/' + item.username + '">';
27
+ el += '<div class="widget-body ds-ghinfo" api="https://' + theme.api_host.ghapi + '/users/' + item.username + '">';
28
28
  if (item.avatar) {
29
29
  el += '<div class="avatar" ><img no-lazy type="img" id="avatar_url" src="' + config.avatar + '"></div>';
30
30
  }
package/layout/layout.ejs CHANGED
@@ -65,6 +65,7 @@ if (theme.style.prefers_theme === 'auto') {
65
65
  html += partial('_partial/menubtn')
66
66
  html += `</div>`
67
67
  html += `<div class="scripts">`
68
+ html += json_ld()
68
69
  html += partial('_partial/scripts')
69
70
  html += `</div>`
70
71
  html += `</body>`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hexo-theme-stellar",
3
- "version": "1.30.4",
3
+ "version": "1.31.0",
4
4
  "description": "Elegant and powerful theme for Hexo.",
5
5
  "main": "package.json",
6
6
  "scripts": {
@@ -18,9 +18,14 @@ hexo.on('generateAfter', () => {
18
18
  });
19
19
 
20
20
  hexo.on('ready', () => {
21
+ if (process.env.HEXO_READY === '1') return;
22
+ process.env.HEXO_READY = '1';
21
23
  const { version, homepage, repository } = require('../../package.json');
22
- hexo.log.info(`Welcome to Stellar ${version}
23
- DOCS ${homepage}
24
- REPO ${repository.url}
24
+ hexo.log.info(`Welcome to \x1b[33mStellar ${version}\x1b[0m
25
+ \x1b[32mDOCS\x1b[0m ${homepage}
26
+ \x1b[32mREPO\x1b[0m ${repository.url}
25
27
  `);
28
+ // version check
29
+ const checkVersion = require('./lib/version-check');
30
+ checkVersion(hexo, { useCache: true });
26
31
  });
@@ -0,0 +1,84 @@
1
+ const https = require('https');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const packageName = 'hexo-theme-stellar';
5
+ const cacheFile = path.join(__dirname, '../../.cache/stellar-version.json');
6
+
7
+ function getLocalVersion() {
8
+ try {
9
+ return require(path.join(__dirname, '../../../package.json')).version;
10
+ } catch {
11
+ return '0.0.0';
12
+ }
13
+ }
14
+
15
+ function fetchLatestVersion(callback) {
16
+ const options = {
17
+ hostname: 'registry.npmjs.org',
18
+ path: `/${encodeURIComponent(packageName)}`,
19
+ headers: {
20
+ 'User-Agent': 'Hexo-Theme-Version-Check'
21
+ }
22
+ };
23
+
24
+ https.get(options, (res) => {
25
+ let rawData = '';
26
+ res.on('data', chunk => rawData += chunk);
27
+ res.on('end', () => {
28
+ try {
29
+ const data = JSON.parse(rawData);
30
+ const latest = data['dist-tags'].latest;
31
+ callback(null, latest);
32
+ } catch (err) {
33
+ callback(err);
34
+ }
35
+ });
36
+ }).on('error', err => callback(err));
37
+ }
38
+
39
+ function shouldCheck() {
40
+ if (!fs.existsSync(cacheFile)) return true;
41
+ try {
42
+ const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
43
+ const time = new Date().toISOString().slice(0, 16); // 精确到分钟,例如 "2025-06-21T15:08"
44
+ return cache.time !== time;
45
+ } catch {
46
+ return true;
47
+ }
48
+ }
49
+
50
+ function writeCache(latestVersion) {
51
+ try {
52
+ fs.mkdirSync(path.dirname(cacheFile), { recursive: true });
53
+ fs.writeFileSync(cacheFile, JSON.stringify({
54
+ time: new Date().toISOString().slice(0, 16),
55
+ latest: latestVersion
56
+ }));
57
+ } catch {
58
+ // 忽略错误
59
+ }
60
+ }
61
+
62
+ module.exports = (ctx, options = { useCache: true }) => {
63
+ if (process.env.STELLAR_VERSION_CHECKED === '1') return;
64
+ process.env.STELLAR_VERSION_CHECKED = '1';
65
+
66
+ const localVersion = getLocalVersion();
67
+ if (options.useCache && !shouldCheck()) return;
68
+
69
+ fetchLatestVersion((err, latest) => {
70
+ if (err) return;
71
+ if (localVersion !== latest) {
72
+ const line = '------------------------------------------------';
73
+ ctx.log.warn(`\x1b[33m${line}\x1b[0m`);
74
+ ctx.log.warn(``);
75
+ ctx.log.warn(` 本地版本: \x1b[33m${localVersion}\x1b[0m >>>> 最新版本: \x1b[32m${latest}\x1b[0m`);
76
+ ctx.log.warn(``);
77
+ ctx.log.warn(` 请尽快升级: npm i ${packageName}@latest`);
78
+ ctx.log.warn(``);
79
+ ctx.log.warn(`\x1b[33m${line}\x1b[0m`);
80
+ }
81
+ if (options.useCache) writeCache(latest);
82
+ });
83
+
84
+ };
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Builds JSON-LD structured data for current page according to its type (page or post).
3
+ *
4
+ * @returns {string} - JSON-LD structured data
5
+ */
6
+ 'use strict';
7
+
8
+ const util = require('hexo-util');
9
+
10
+ hexo.extend.helper.register('json_ld', function(args) {
11
+ const page = this.page;
12
+ const config = this.config;
13
+ const structured_data = this.theme.structured_data;
14
+ const authorEmail = config.email;
15
+ const authorImage = config.avatar || (authorEmail ? this.gravatar(authorEmail) : null);
16
+
17
+ const author = {
18
+ '@type': 'Person',
19
+ name: config.author,
20
+ sameAs: structured_data.sameAs || []
21
+ };
22
+ // Google does not accept `Person` as item type for the publisher property
23
+ const publisher = Object.assign({}, author, {'@type': 'Organization'});
24
+ let schema = {};
25
+
26
+ if (authorImage) {
27
+ author.image = authorImage;
28
+ publisher.image = authorImage;
29
+ publisher.logo = {
30
+ '@type': 'ImageObject',
31
+ url: authorImage
32
+ };
33
+ }
34
+
35
+ if (this.is_post()) {
36
+ schema = {
37
+ '@context': 'https://schema.org',
38
+ '@type': 'BlogPosting',
39
+ author: author,
40
+ // articleBody: this.strip_html(page.content),
41
+ dateCreated: page.date.format(),
42
+ dateModified: page.updated.format(),
43
+ datePublished: page.date.format(),
44
+ description: this.strip_html(page.excerpt),
45
+ headline: page.title,
46
+ mainEntityOfPage: {
47
+ '@type': 'WebPage',
48
+ '@id': this.url_for(page.permalink)
49
+ },
50
+ publisher,
51
+ url: this.url_for(page.permalink)
52
+ };
53
+
54
+ if (page.tags && page.tags.length > 0) {
55
+ schema.keywords = page.tags.map((tag) => tag.name).join(', ');
56
+ }
57
+
58
+ let images = [];
59
+ if (page.photos && page.photos.length > 0) {
60
+ images = images.concat(page.photos);
61
+ }
62
+
63
+ if (page.cover?.length > 0) {
64
+ images = images.unshift(page.cover);
65
+ } else if (page.banner?.length > 0) {
66
+ images = images.unshift(page.banner);
67
+ }
68
+
69
+ schema.thumbnailUrl = page.cover || page.banner;
70
+ schema.image = images;
71
+
72
+ } else if (this.is_page() || this.is_home()) {
73
+
74
+ const url = this.is_home() ? config.url : this.url_for(page.permalink);
75
+ schema = {
76
+ '@context': 'https://schema.org',
77
+ '@type': 'Website',
78
+ '@id': url,
79
+ author: author,
80
+ name: page.title || config.title,
81
+ description: config.description,
82
+ url: url
83
+ };
84
+
85
+ if (config.keywords && config.keywords.length) {
86
+ schema.keywords = config.keywords.join(', ');
87
+ }
88
+ if (!this.is_home()) {
89
+
90
+ if (page.excerpt || page.description) {
91
+ schema.description = this.strip_html(page.description || page.excerpt);
92
+ } else {
93
+ schema.description = util.truncate(this.strip_html(page.content), {length: 200});
94
+ }
95
+
96
+ }
97
+
98
+ } else {
99
+
100
+ // default to WebPage for other layouts
101
+ schema = {
102
+ '@context': 'https://schema.org',
103
+ '@type': 'Website',
104
+ '@id': config.url,
105
+ author: author,
106
+ name: config.title,
107
+ description: config.description,
108
+ url: config.url
109
+ };
110
+
111
+ }
112
+
113
+ return `<script type="application/ld+json">${JSON.stringify(schema)}</script>`;
114
+ });
@@ -9,6 +9,7 @@
9
9
 
10
10
  module.exports = ctx => function(args) {
11
11
  var args = ctx.args.map(args, ['repo', 'api', 'size'], ['group'])
12
+ const host = ctx.theme.config.api_host.ghraw
12
13
  if (args.size == null) {
13
14
  args.size = 's'
14
15
  }
@@ -16,7 +17,7 @@ module.exports = ctx => function(args) {
16
17
  if (args.api) {
17
18
  api = args.api
18
19
  } else if (args.repo) {
19
- api = 'https://api.vlts.cc/output_data/v2/' + args.repo
20
+ api = `https://${host}/${args.repo}/output/v2/data.json`
20
21
  }
21
22
 
22
23
  var el = ''
@@ -9,11 +9,12 @@
9
9
 
10
10
  module.exports = ctx => function(args) {
11
11
  args = ctx.args.map(args, ['repo', 'api', 'posts'], ['group'])
12
+ const host = ctx.theme.config.api_host.ghraw
12
13
  var api
13
14
  if (args.api) {
14
15
  api = args.api
15
16
  } else if (args.repo) {
16
- api = 'https://api.vlts.cc/output_data/v2/' + args.repo
17
+ api = `https://${host}/${args.repo}/output/v2/data.json`
17
18
  }
18
19
 
19
20
  var el = `<div class="tag-plugin ${args.posts ? 'users-posts-wrap' : 'users-wrap'}">`
@@ -17,6 +17,7 @@ module.exports = ctx => function(args) {
17
17
  var params = ['show_owner', 'theme', 'title_color', 'text_color', 'icon_color', 'bg_color', 'hide_border', 'cache_seconds', 'locale']
18
18
  args = ctx.args.map(args, params, ['repo'])
19
19
  const path = args.repo
20
+ const host = ctx.theme.config.api_host.ghcard
20
21
  var el = ''
21
22
  el += '<div class="tag-plugin ghcard">'
22
23
  el += '<a class="ghcard" rel="external nofollow noopener noreferrer" href="https://github.com/' + path + '">'
@@ -24,10 +25,10 @@ module.exports = ctx => function(args) {
24
25
  if (path.includes('/')) {
25
26
  // is repo
26
27
  const ps = path.split('/')
27
- url += 'https://github-readme-stats.xaoxuu.com/api/pin/?username=' + ps[0] + '&repo=' + ps[1]
28
+ url = `https://${host}/api/pin/?username=${ps[0]}&repo=${ps[1]}`
28
29
  } else {
29
30
  // is user
30
- url += 'https://github-readme-stats.xaoxuu.com/api/?username=' + path
31
+ url = `https://${host}/api/?username=${path}`
31
32
  }
32
33
  url += '&' + ctx.args.joinURLParams(args, params)
33
34
  if (!url.includes('&show_owner=')) {
@@ -9,6 +9,7 @@
9
9
 
10
10
  module.exports = ctx => function(args) {
11
11
  var args = ctx.args.map(args, ['repo', 'api', 'size'], ['group'])
12
+ const host = ctx.theme.config.api_host.ghraw
12
13
  if (args.size == null) {
13
14
  args.size = 'm'
14
15
  }
@@ -16,7 +17,7 @@ module.exports = ctx => function(args) {
16
17
  if (args.api) {
17
18
  api = args.api
18
19
  } else if (args.repo) {
19
- api = 'https://api.vlts.cc/output_data/v2/' + args.repo
20
+ api = `https://${host}/${args.repo}/output/v2/data.json`
20
21
  }
21
22
 
22
23
  var el = ''
@@ -9,11 +9,12 @@
9
9
 
10
10
  module.exports = ctx => function(args) {
11
11
  args = ctx.args.map(args, ['repo', 'api'], ['group'])
12
+ const host = ctx.theme.config.api_host.ghraw
12
13
  var api
13
14
  if (args.api) {
14
15
  api = args.api
15
16
  } else if (args.repo) {
16
- api = 'https://api.vlts.cc/output_data/v2/' + args.repo
17
+ api = `https://${host}/${args.repo}/output/v2/data.json`
17
18
  }
18
19
 
19
20
  var el = '<div class="tag-plugin sites-wrap">'
@@ -32,8 +32,8 @@ module.exports = ctx => function (args) {
32
32
  </div>
33
33
  `
34
34
  }
35
- return `<div class="tag-plugin video" style="max-width:${args.width};">
36
- <video controls preload>
35
+ return `<div class="tag-plugin video-player" style="max-width:${args.width};">
36
+ <video controls preload playsinline webkit-playsinline>
37
37
  <source src="${args.src}" type="${args.type || 'video/mp4'}">Your browser does not support the video tag.
38
38
  </video>
39
39
  </div>
@@ -0,0 +1,32 @@
1
+ .canonical-tip
2
+ position: fixed
3
+ text-align: center
4
+ bottom: 4rem
5
+ left: 50%
6
+ transform: translateX(-50%)
7
+ max-width: 90%
8
+ background-color: var(--block-hover)
9
+ color: var(--text-p1)
10
+ font-size: 1rem
11
+ font-family: #fff8e1
12
+ z-index: 9999999
13
+ box-shadow: 0 2px 8px rgba(black, .1), 0 4px 16px rgba(black, .1), 0 8px 32px rgba(black, .1)
14
+ border-radius: 6px
15
+ .headline
16
+ font-size: $fsh2
17
+ a
18
+ display: block
19
+ color: inherit
20
+ padding: 1rem 2rem
21
+
22
+ .canonical-tip.unofficial
23
+ background-color: hsl(5, 100%, 60%)
24
+ color: white
25
+ animation: breathe 3s ease-in-out infinite
26
+ @keyframes breathe
27
+ 0%
28
+ background-color: hsl(5, 100%, 60%)
29
+ 50%
30
+ background-color: hsl(5, 100%, 30%)
31
+ 100%
32
+ background-color: hsl(5, 100%, 60%)
@@ -10,14 +10,13 @@
10
10
  .float-panel
11
11
  position: sticky
12
12
  grid-column-end: span 3
13
- padding: 4px
14
13
  --inset: 2rem
15
14
  right: 0
16
15
  bottom: calc(var(--inset) * 2)
17
16
  margin-left: auto
18
17
  margin-right: var(--inset)
19
18
  float: right
20
- z-index: 10
19
+ z-index: 999999
21
20
  display: flex
22
21
  justify-content: center
23
22
  flex-direction: column
@@ -26,6 +25,9 @@
26
25
  margin-right: 3rem
27
26
  overflow: hidden
28
27
  trans1: all
28
+ &:before,&:after
29
+ border-radius: 64px
30
+
29
31
  newblur()
30
32
 
31
33
 
@@ -34,8 +36,8 @@
34
36
  cursor: pointer
35
37
  color: var(--text)
36
38
  background: none
37
- width: 40px
38
- height: 40px
39
+ width: 48px
40
+ height: 48px
39
41
  line-height: 0
40
42
  font-size: 28px
41
43
  margin: 0
@@ -20,10 +20,11 @@
20
20
  .sidebg
21
21
  pointer-events: none
22
22
  position: absolute
23
- top: 0
24
- bottom: 0
25
- left: 0
26
- right: 0
23
+ --inset: 0
24
+ top: var(--inset)
25
+ bottom: var(--inset)
26
+ left: var(--inset)
27
+ right: var(--inset)
27
28
  background-position: center
28
29
  background-size: cover
29
30
  border-radius: $border-card-l
@@ -32,8 +33,7 @@
32
33
  --brightness: 100%
33
34
  if hexo-config('style.leftbar') && hexo-config('style.leftbar.background-image')
34
35
  if hexo-config('style.leftbar.blur-px')
35
- left: 20%
36
- right: 20%
36
+ --inset: 32px
37
37
  background-image: convert(hexo-config('style.leftbar.background-image'))
38
38
  filter: saturate(var(--saturate)) brightness(var(--brightness)) blur(var(--blur-px))
39
39
  @media screen and (prefers-color-scheme: dark)
@@ -44,13 +44,17 @@
44
44
  background: convert(hexo-config('style.leftbar.background'))
45
45
 
46
46
  .leftbar-container
47
- height: 'calc(%s - 16px)' % 100vh
47
+ height: 'calc(%s - 80px)' % 100vh
48
48
  display: flex
49
49
  flex-direction: column
50
50
  word-break: break-all
51
51
  text-align: justify
52
52
  border-radius: $border-card-l
53
53
  overflow: hidden
54
+ >.widgets
55
+ mask: linear-gradient(transparent, white, 10%, white, 90%, transparent)
56
+ >*
57
+ z-index 1
54
58
  &:before,&:after
55
59
  position: absolute
56
60
  pointer-events: none
@@ -65,7 +69,7 @@
65
69
  background: rgba(white, 0.5)
66
70
  box-shadow: inset 0 0 32px 1px rgba(white, 0.5)
67
71
  backdrop-filter: saturate(300%)
68
- mask: linear-gradient(black, rgba(black, 0.5), 80%, transparent, 90%, transparent)
72
+ mask: linear-gradient(black, rgba(black, 0.5), 70%, transparent, 90%, transparent)
69
73
 
70
74
  @media screen and (prefers-color-scheme: dark)
71
75
  background: rgba(white, 0.05)
@@ -76,11 +80,7 @@
76
80
  overflow: hidden
77
81
  background: var(--alpha100)
78
82
  .sidebg
79
- left: 0
80
- right: 0
83
+ --inset: 0
81
84
  --saturate: 300%
82
-
83
-
84
- @media screen and (max-width: $device-mobile-max) and (prefers-color-scheme: dark)
85
- .l_left .leftbar-container:after
86
- background: var(--alpha20)
85
+ .leftbar-container:after
86
+ background: var(--alpha20)
@@ -1,4 +1,4 @@
1
- .tag-plugin.video
1
+ .tag-plugin.video-player
2
2
  line-height: 0
3
3
  margin: auto
4
4
  video, iframe
@@ -6,9 +6,7 @@
6
6
  border-radius: $border-bar
7
7
  color: var(--text-p1)
8
8
  trans1 background
9
- &.active
10
- background: var(--alpha50)
11
- &:hover
9
+ &.active,&:hover
12
10
  background: var(--alpha100)
13
11
  a+a
14
12
  margin-top: 2px
@@ -134,7 +134,7 @@ hoverable-card()
134
134
  ondark()
135
135
 
136
136
  newblur()
137
- box-shadow: 0 0 2px rgba(black, .05), 0 0 8px rgba(black, .05)
137
+ box-shadow: 0 0 2px rgba(black, .02), 0 2px 8px rgba(black, .03), 0 4px 16px rgba(black, .05)
138
138
  trans1: all
139
139
  &:before,&:after
140
140
  z-index -1
@@ -149,12 +149,12 @@ newblur()
149
149
  &:before
150
150
  backdrop-filter: blur(8px)
151
151
  &:after
152
- --blur-px: 16px
152
+ --blur-px: 32px
153
153
  --blur-sat: 500%
154
- background: var(--alpha50)
154
+ background: var(--alpha20)
155
155
  backdrop-filter: saturate(var(--blur-sat)) blur(var(--blur-px))
156
- mask: linear-gradient(rgba(white, 1), 10%, rgba(white, 0.2), 90%, rgba(white, 1)), linear-gradient(90deg, rgba(white, 1), 10%, rgba(white, 0.2), 90%, rgba(white, 1))
157
- box-shadow: inset 0 0 8px 1px white
156
+ mask: linear-gradient(rgba(white, 1), 10%, rgba(white, 0.1), 90%, rgba(white, 1)), linear-gradient(90deg, rgba(white, 1), 10%, rgba(white, 0.1), 90%, rgba(white, 1))
157
+ box-shadow: inset 0 0 4px 1px rgba(white, 0.5)
158
158
  trans1 all
159
159
  @media (prefers-color-scheme: dark)
160
160
  opacity 0.4
@@ -121,11 +121,11 @@ _common_root()
121
121
  _dark_root()
122
122
 
123
123
  @media (prefers-color-scheme: dark)
124
- :root:not([color-scheme])
124
+ :root:not([data-theme])
125
125
  img,
126
126
  .md-text .tag-plugin.chat>.content .chatcell .user-main .talk.file>.content>.top>.right>svg.icon>path
127
127
  filter: brightness(75%)!important
128
- [color-scheme='dark']
128
+ [data-theme='dark']
129
129
  img,
130
130
  .md-text .tag-plugin.chat>.content .chatcell .user-main .talk.file>.content>.top>.right>svg.icon>path
131
131
  filter: brightness(75%)!important
package/source/js/main.js CHANGED
@@ -200,6 +200,55 @@ const init = {
200
200
  window.dispatchEvent(new Event('tabs:register'));
201
201
  },
202
202
 
203
+ canonicalCheck: () => {
204
+ const canonical = window.canonical;
205
+ function showTip(isOfficial = false) {
206
+ const meta = document.createElement('meta');
207
+ meta.name = 'robots';
208
+ meta.content = 'noindex, nofollow';
209
+ document.head.appendChild(meta);
210
+ const notice = document.createElement('div');
211
+ const originalURL = `https://${canonical.originalHost}`;
212
+ if (isOfficial) {
213
+ notice.className = 'canonical-tip official';
214
+ notice.innerHTML = `
215
+ <a href="${originalURL}" target="_self" rel="noopener noreferrer">
216
+ 本站为官方备用站,仅供应急。主站:${originalURL}
217
+ </a>
218
+ `;
219
+ } else {
220
+ notice.className = 'canonical-tip unofficial';
221
+ notice.innerHTML = `
222
+ <a href="${originalURL}" target="_self" rel="noopener noreferrer">
223
+ <div class="headline icon">☠️</div>
224
+ 本站为非法克隆站,请前往官方源站访问。<br>
225
+ 源站:${originalURL}
226
+ </a>
227
+ `;
228
+ }
229
+ document.body.appendChild(notice);
230
+ }
231
+ if (!canonical.originalHost) return;
232
+ const canonicalTag = document.querySelector('link[rel="canonical"]');
233
+ if (!canonicalTag) {
234
+ showTip(false);
235
+ return;
236
+ }
237
+ const canonicalURL = new URL(canonicalTag.href);
238
+ const currentURL = new URL(window.location.href);
239
+ const canonicalHost = canonicalURL.hostname.replace(/^www\./, '');
240
+ const currentHost = currentURL.hostname.replace(/^www\./, '');
241
+ if (currentHost == 'localhost') return;
242
+ const encodedCanonicalHost = window.btoa(canonicalHost);
243
+ const encodedCurrentHost = window.btoa(currentHost);
244
+ const isCanonicalHostValid = canonical.encoded === encodedCanonicalHost;
245
+ const isCurrentHostValid = canonical.encoded === encodedCurrentHost;
246
+ if (isCanonicalHostValid && isCurrentHostValid) {
247
+ return;
248
+ }
249
+ showTip(canonical.officialHosts?.includes(currentHost));
250
+ }
251
+
203
252
  }
204
253
 
205
254
 
@@ -208,3 +257,4 @@ init.toc()
208
257
  init.sidebar()
209
258
  init.relativeDate(document.querySelectorAll('#post-meta time'))
210
259
  init.registerTabsTag()
260
+ init.canonicalCheck()
@@ -167,7 +167,7 @@ function extToMimes(ext) {
167
167
  function downloadFileEvent(fileDoms) {
168
168
  for (let i = 0; i < fileDoms.length; ++i) {
169
169
  let fileDom = fileDoms[i];
170
- const api = ctx.tag_plugins.chat.api + '?type=file&url=' || 'https://api.vlts.cc/site_info/v1?type=file&url=';
170
+ const api = ctx.tag_plugins.chat.api + '?type=file&url=';
171
171
  const obverser = new IntersectionObserver((entries, observer) => {
172
172
  utils.requestAnimationFrame(()=>{
173
173
  entries.filter((entry)=>{return entry.isIntersecting}).sort((a,b)=>a.intersectionRect.y !== b.intersectionRect.y ? a.intersectionRect.y - b.intersectionRect.y : a.intersectionRect.x - b.intersectionRect.x).forEach((entry, index) => {
@@ -3,7 +3,6 @@ utils.jq(() => {
3
3
  for (var i = 0; i < els.length; i++) {
4
4
  const el = els[i];
5
5
  const src = `${el.getAttribute('src')}?t=${new Date().getTime()}`;
6
- console.log('src', src);
7
6
 
8
7
  utils.request(el, src, async resp => {
9
8
  const data = await resp.text();