diff --git a/README.md b/README.md
index ff0173a..7aacdd6 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,40 @@
# hexo-theme-stellar
+
Elegant and powerful theme for Hexo.
+
+## Getting Started
+
+Check your environment:
+
+```yaml
+Hexo: 5.3.0
+hexo-cli: 4.2.0
+node.js: 14.15.4 LTS
+npm: 6.14.10 LTS
+```
+
+Edit your `_config.yml`:
+
+```yaml
+theme: stellar
+```
+
+Install Stellar in terminal:
+
+```bash
+npm i hexo-theme-stellar
+```
+
+## Usage
+
+See docs: https://xaoxuu.com/wiki/stellar
+
+## Examples
+
+https://xaoxuu.com
+
+## Feedback
+
+Issues: https://github.com/xaoxuu/hexo-theme-stellar/issues/
+
+Discussions: https://github.com/xaoxuu/hexo-theme-stellar/discussions/
diff --git a/_config.yml b/_config.yml
new file mode 100755
index 0000000..ae9afca
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1,164 @@
+######## Stellar info ########
+stellar:
+ version: '1.0.0-rc.1'
+ homepage: https://xaoxuu.com/wiki/stellar/
+ repo: https://github.com/xaoxuu/hexo-theme-stellar
+ # The cdn version needs to be consistent with the stellar version
+ cdn_css: # https://cdn.jsdelivr.net/npm/hexo-theme-stellar@1.0/source/css/main.min.css
+ cdn_js: # https://cdn.jsdelivr.net/npm/hexo-theme-stellar@1.0/source/js/main.min.js
+
+
+######## head tags ########
+head:
+ open_graph: true
+ twitter: false
+
+
+######## Sidebar ########
+sidebar:
+ logo:
+ avatar: # default is config.avatar
+ title: # default is config.title
+ logo_action:
+ avatar: /about/
+ title: /
+ menu:
+ - class: post # post, wiki, about
+ name: btn.blog
+ url: /
+ - class: wiki
+ name: btn.wiki
+ url: /wiki/
+ - class: friends
+ name: 友链
+ url: /friends/
+ - class: about
+ name: 关于
+ url: /about/
+ # Sidebar widgets
+ widgets:
+ # Recent update
+ recent:
+ layout: recent
+ rss: /atom.xml # npm i hexo-generator-feed
+ limit: 5 # Count of posts
+ # TOC (valid only in layout:post/wiki)
+ toc:
+ layout: toc
+ list_number: false
+ min_depth: 2
+ max_depth: 5
+ # welcome
+ welcome:
+ layout: markdown
+ title: 欢迎
+ content:
+ - '欢迎光临小站,[Stellar](https://github.com/xaoxuu/hexo-theme-stellar/) 是一个支持 wiki 的 hexo 主题,适合综合型站点使用。同时也拥有简约而精美的视觉设计和丰富的标签插件,帮助您简单从容地应对各种场合。'
+
+
+
+######## Article ########
+article:
+ # 如果没有指定 excerpt 和 description,将自动取多长的内容作为文章摘要?
+ auto_excerpt: 200
+ # 相关文章,需要安装插件 (for layout: post)
+ # npm i hexo-related-popular-posts
+ related_posts:
+ enable: false
+ title: 您可能感兴趣的文章
+ max_count: 5
+ placeholder_img: https://cdn.jsdelivr.net/gh/volantis-x/cdn-wallpaper-minimalist/2020/046.jpg
+ # 分享文章
+ share:
+ qq: https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qq.png
+ qzone: https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qzone.png
+ weibo: https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/weibo.png
+ # 需要安装插件 npm i hexo-helper-qrcode
+ wechat: https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/wechat.png
+
+
+######## Comments ########
+comments:
+ service: utterances # utterances
+ # utterances
+ # https://utteranc.es/
+ utterances:
+ repo: xxx/xxx
+ issue-term: pathname
+ issue-number:
+ theme:
+ light: github-light
+ dark: github-dark
+ label: ✨💬✨
+ crossorigin: anonymous
+
+
+######## Footer ########
+footer:
+ social:
+ - icon: ' '
+ url: /atom.xml
+ - icon: ' '
+ url: https://github.com/xaoxuu
+ - icon: ' '
+ url: https://music.163.com/#/user/home?id=63035382
+ - icon: ' '
+ url: /comments/
+ license: 'CC BY SA 4.0'
+ source:
+ where: GitHub
+ url: https://github.com/xaoxuu/site-source
+
+
+######## Tag Plugins ########
+tag_plugins:
+ # {% note text %}
+ note:
+ default_color: ''
+ # {% checkbox %}
+ checkbox:
+ interactive: false # enable interactive for user
+ # {% link title, url, img %}
+ link:
+ default_img: https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/256/safari.png
+
+
+######## JS Plugins ########
+plugins:
+ ## required plugins ##
+ # jquery
+ jquery: https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js
+
+ ## lazyload plugins ##
+ # fancybox
+ fancybox: https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css
+ # swiper
+ swiper:
+ enable: true
+ css: https://unpkg.com/swiper/swiper-bundle.min.css
+ js: https://unpkg.com/swiper/swiper-bundle.min.js
+ ## optional plugins ##
+ # preload
+ preload:
+ enable: #true
+ service: instant_page # instant_page, flying_pages
+ instant_page: https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@2/js/instant_page.js
+ flying_pages: https://cdn.jsdelivr.net/gh/gijo-varghese/flying-pages@2.1.2/flying-pages.min.js
+
+ # image lazyload
+ # https://www.npmjs.com/package/vanilla-lazyload
+ lazyload:
+ enable: true
+ js: https://cdn.jsdelivr.net/npm/vanilla-lazyload@17.3.1/dist/lazyload.min.js
+ onlypost: false
+ loadingImg: # https://7.dusays.com/2021/02/14/e45d2469cdeaf.svg
+ blurIn: true # 模糊加载效果 (loadingImg为空时有效)
+
+ # https://scrollrevealjs.org/api/reveal.html
+ scrollreveal:
+ enable: true
+ js: https://cdn.jsdelivr.net/npm/scrollreveal@4.0.7/dist/scrollreveal.min.js
+ distance: 16px
+ duration: 800 # ms
+ interval: 80 # ms
+ scale: 1 # 0.1~1
diff --git a/languages/en.yml b/languages/en.yml
new file mode 100755
index 0000000..0fddc1d
--- /dev/null
+++ b/languages/en.yml
@@ -0,0 +1,46 @@
+btn:
+ home: Home
+ blog: Blog
+ wiki: Wiki
+ recent_publish: Recent
+ all_wiki: All
+ category: Category
+ categories: Categories
+ tag: Tag
+ tags: Tags
+ archives: Archives
+ learn_more: Learn more
+ getting_started: Getting Started
+
+meta:
+ recent_update: Recent Update
+ toc: TOC
+ read_prev: PREV
+ read_next: NEXT
+ older: Older
+ newer: Newer
+ references: References
+ comment_title: Join the discussion
+ back_to_top: Back to top
+ more: 'More %s'
+
+footer:
+ license: 'Unless otherwise specified, this site adopts the CC BY SA 4.0 license agreement.'
+ info_not_open_source: 'This site was created by %s, using [Stellar](https://github.com/xaoxuu/hexo-theme-stellar) as the theme.'
+ info_open_source: 'This site was created by %s, using [Stellar](https://github.com/xaoxuu/hexo-theme-stellar) as the theme. You can find the source code of this site in [%s](%s).'
+
+page:
+ error:
+ what: Page Not Found
+ why: The address may be entered incorrectly or the address has been deleted
+ action: Back to Home
+ categories: '%s Categories'
+ tags: '%s Tags'
+ archives: '%s Years, %s Archives'
+
+symbol:
+ comma: ", "
+ period: ". "
+ colon: ": "
+ brackets_l: "("
+ brackets_r: ")"
diff --git a/languages/zh-CN.yml b/languages/zh-CN.yml
new file mode 100755
index 0000000..f26c956
--- /dev/null
+++ b/languages/zh-CN.yml
@@ -0,0 +1,46 @@
+btn:
+ home: 主页
+ blog: 文章
+ wiki: 项目
+ recent_publish: 最新发布
+ all_wiki: 所有项目
+ category: 分类
+ categories: 分类
+ tag: 标签
+ tags: 标签
+ archives: 归档
+ learn_more: 了解详情
+ getting_started: 开始使用
+
+meta:
+ recent_update: 最近更新
+ toc: 本文目录
+ read_prev: 回顾上一篇
+ read_next: 接下来阅读
+ older: 较早文章
+ newer: 较新文章
+ references: 参考资料
+ comment_title: 快来参与讨论吧
+ back_to_top: 回到顶部
+ more: '更多%s'
+
+footer:
+ license: '除非特别说明,本站采用 CC BY SA 4.0 许可协议。'
+ info_not_open_source: '本站由 %s 创建,使用 [Stellar](https://github.com/xaoxuu/hexo-theme-stellar) 作为主题。'
+ info_open_source: '本站由 %s 创建,使用 [Stellar](https://github.com/xaoxuu/hexo-theme-stellar) 作为主题,您可以在 [%s](%s) 找到本站源码。'
+
+page:
+ error:
+ what: 很抱歉,您访问的页面不存在
+ why: 可能是输入地址有误或该地址已被删除
+ action: 返回主页
+ categories: '共%s个分类'
+ tags: '共%s个标签'
+ archives: 'TA在%s年间写了%s篇文章'
+
+symbol:
+ comma: ","
+ period: "。"
+ colon: ":"
+ brackets_l: "("
+ brackets_r: ")"
diff --git a/languages/zh-TW.yml b/languages/zh-TW.yml
new file mode 100755
index 0000000..26c1b9a
--- /dev/null
+++ b/languages/zh-TW.yml
@@ -0,0 +1,46 @@
+btn:
+ home: 主頁
+ blog: 博客
+ wiki: 項目
+ recent_publish: 最新發布
+ all_wiki: 所有項目
+ category: 分類
+ categories: 分類
+ tag: 標籤
+ tags: 標籤
+ archives: 歸檔
+ learn_more: 了解詳情
+ getting_started: 開始使用
+
+meta:
+ recent_update: 最近更新
+ toc: 本文目錄
+ read_prev: 回顧上一篇
+ read_next: 接下來閱讀
+ older: 較早文章
+ newer: 較新文章
+ references: 參考資料
+ comment_title: 快來參與討論吧
+ back_to_top: 回到頂部
+ more: '更多%s'
+
+footer:
+ license: '除非特別說明,本站採用 CC BY SA 4.0 許可協議。'
+ info_not_open_source: '本站由 %s 創建,使用 [Stellar](https://github.com/xaoxuu/hexo-theme-stellar) 作為主題。'
+ info_open_source: '本站由 %s 創建,使用 [Stellar](https://github.com/xaoxuu/hexo-theme-stellar) 作為主題,您可以在 [%s](%s) 找到本站源碼。'
+
+page:
+ error:
+ what: 很抱歉,您訪問的頁面不存在
+ why: 可能是輸入地址有誤或該地址已被刪除
+ action: 返回主頁
+ categories: '共%s個分類'
+ tags: '共%s個標籤'
+ archives: 'TA在%s年間寫了%s篇文章'
+
+symbol:
+ comma: ","
+ period: "。"
+ colon: ":"
+ brackets_l: "("
+ brackets_r: ")"
diff --git a/layout/404.ejs b/layout/404.ejs
new file mode 100755
index 0000000..7f99b93
--- /dev/null
+++ b/layout/404.ejs
@@ -0,0 +1,19 @@
+<%
+page.class = '404';
+page.layout = '404';
+page.comment_title = '';
+page.comment_id = '1';
+%>
+
+
+
+
+ <%- __('page.error.what') %>
+
+
+
+ <%- __('page.error.why') %>
+
+ <%- __('page.error.action') %>
+
+<%- partial('_partial/plugins/comments/layout') %>
diff --git a/layout/_partial/article/read_next.ejs b/layout/_partial/article/read_next.ejs
new file mode 100644
index 0000000..9a15b78
--- /dev/null
+++ b/layout/_partial/article/read_next.ejs
@@ -0,0 +1,89 @@
+<%
+function layoutDiv() {
+ var prev,next;
+ var title = '';
+ var title_prev = '';
+ var title_next = '';
+ if (page.layout == 'post') {
+ prev = page.prev;
+ next = page.next;
+ title = __('meta.read_next');
+ title_prev = __('meta.newer');
+ title_next = __('meta.older');
+ } else if (page.layout == 'wiki' && page.wiki && page.wiki.length > 0) {
+ title = page.wiki;
+ title_prev = __('meta.read_prev');
+ title_next = __('meta.read_next');
+ var wikis = [];
+ wikis = site.pages.filter(function (p) {
+ if (p.layout == 'wiki' && p.wiki && p.wiki == page.wiki) {
+ if (p.order == undefined) {
+ p.order = 0;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }).sort('order');
+ wikis.forEach((p, i) => {
+ if (p.order < page.order) {
+ if (prev == undefined || p.order > prev.order) {
+ prev = p;
+ }
+ } else if (p.order > page.order) {
+ if (next == undefined || p.order < next.order) {
+ next = p;
+ }
+ }
+ });
+ }
+ let wrapper = '';
+ if (prev || next) {
+ wrapper += '
';
+ wrapper += '';
+ wrapper += '
';
+ if (page.layout == 'wiki') {
+ if (next) {
+ wrapper += '';
+ }
+ if (prev) {
+ wrapper += '';
+ }
+ } else {
+ if (prev) {
+ wrapper += '';
+ }
+ if (next) {
+ wrapper += '';
+ }
+ }
+
+ wrapper += ' ';
+ wrapper += '
';
+ }
+ return wrapper;
+}
+%>
+<%- layoutDiv(); %>
diff --git a/layout/_partial/article/references.ejs b/layout/_partial/article/references.ejs
new file mode 100644
index 0000000..e9ff6f2
--- /dev/null
+++ b/layout/_partial/article/references.ejs
@@ -0,0 +1,23 @@
+<%
+function layoutDiv() {
+ if (page.references && page.references.length > 0) {
+ let wrapper = '';
+ wrapper += '';
+ wrapper += '';
+ wrapper += '
';
+ wrapper += '
';
+ return wrapper;
+ }
+}
+%>
+<%- layoutDiv(); %>
diff --git a/layout/_partial/article/related_posts.ejs b/layout/_partial/article/related_posts.ejs
new file mode 100644
index 0000000..3b180e5
--- /dev/null
+++ b/layout/_partial/article/related_posts.ejs
@@ -0,0 +1,5 @@
+<% let post = page; %>
+<%-
+popular_posts_wrapper(
+ popular_posts_json({ maxCount: 5 , ulClass: 'related-posts' , PPMixingRate: 0.2 , isImage: true , isExcerpt: true} , post )
+) %>
diff --git a/layout/_partial/article/wiki_cover.ejs b/layout/_partial/article/wiki_cover.ejs
new file mode 100644
index 0000000..b1fb595
--- /dev/null
+++ b/layout/_partial/article/wiki_cover.ejs
@@ -0,0 +1,51 @@
+<%
+function layoutWikiCover() {
+ if (page.layout != 'wiki') {
+ return '';
+ }
+ if (page.cover == undefined || page.cover == false || page.cover == '[]') {
+ return '';
+ }
+ var cover = page.cover;
+ if (page.cover == true) {
+ cover = ['logo', 'title', 'description'];
+ }
+ var el = '';
+ el += '';
+ el += '
';
+ if (page.logo && cover.includes('logo')) {
+ el += '';
+ var height = '';
+ var src = '';
+ page.logo.split(' ').forEach(function(item) {
+ if (item.includes('px')) {
+ height = item;
+ } else {
+ src = item;
+ }
+ });
+ if (height.length > 0) {
+ el += '
';
+ } else {
+ el += '
';
+ }
+ el += '
';
+ }
+ if (page.title && cover.includes('title')) {
+ el += '';
+ el += '' + (page.wiki || page.title) + ' ';
+ el += '
';
+ }
+ if (page.description && cover.includes('description')) {
+ el += '' + page.description + '
';
+ }
+ el += '';
+ el += ' ';
+ el += '
';
+ el += ' ';
+ return el;
+}
+%>
+<%- layoutWikiCover() %>
diff --git a/layout/_partial/footer.ejs b/layout/_partial/footer.ejs
new file mode 100755
index 0000000..894dcda
--- /dev/null
+++ b/layout/_partial/footer.ejs
@@ -0,0 +1,18 @@
+<%
+function layoutDiv() {
+ var wrapper = '';
+ wrapper += '
';
+ if (theme.footer.license) {
+ wrapper += markdown(__('footer.license', theme.footer.license));
+ }
+ const author = '[' + config.author + '](' + config.url + config.root + ')';
+ if (theme.footer.source && theme.footer.source.where && theme.footer.source.url) {
+ wrapper += markdown(__('footer.info_open_source', author, theme.footer.source.where, theme.footer.source.url));
+ } else {
+ wrapper += markdown(__('footer.info_not_open_source', author));
+ }
+ wrapper += '';
+ return wrapper;
+}
+%>
+<%- layoutDiv() %>
\ No newline at end of file
diff --git a/layout/_partial/head.ejs b/layout/_partial/head.ejs
new file mode 100755
index 0000000..d97de3a
--- /dev/null
+++ b/layout/_partial/head.ejs
@@ -0,0 +1,134 @@
+<%
+function genrateRobots() {
+ var el = '';
+ if (page.robots) {
+ el += ' ';
+ } else if (is_home() == false) {
+ if (['post', 'wiki', 'index'].includes(page.layout) == false) {
+ el += ' ';
+ }
+ }
+ return el;
+}
+
+function getTitle() {
+ if (page.seo_title || page.title || page.wiki || page.layout) {
+ return (page.seo_title || page.title || page.wiki || page.layout) + ' - ' + config.title;
+ } else if (page.category) {
+ return __('btn.category') + __('symbol.colon') + page.category + ' - ' + config.title;
+ } else if (page.tag) {
+ return __('btn.tag') + __('symbol.colon') + page.tag + ' - ' + config.title;
+ } else {
+ return config.title;
+ }
+ return '';
+}
+function getKeywords() {
+ if (page.keywords) {
+ return page.keywords;
+ } else if (page.tags && page.tags.length > 0) {
+ return page.tags.map(function(t){return t.name}).join(',');
+ }
+ return '';
+}
+function getDescription() {
+ if (page.description) {
+ return page.description;
+ } else {
+ if (['post', 'wiki'].includes(page.layout)) {
+ if (page.excerpt && page.excerpt.length > 0) {
+ return strip_html(page.excerpt);
+ } else if (page.content && page.content.length > 0) {
+ return truncate(strip_html(page.content), {length: 160});
+ }
+ }
+ }
+ return '';
+}
+function genrateKeywords() {
+ const keywords = getKeywords();
+ if (keywords && keywords.length > 0) {
+ return ' ';
+ } else {
+ return '';
+ }
+}
+function genrateDescription() {
+ const description = getDescription();
+ if (description && description.length > 0) {
+ return ' ';
+ } else {
+ return '';
+ }
+}
+function genrateOpenGraph() {
+ if (theme.head.open_graph == false) {
+ return '';
+ }
+ var el = '';
+ el += ' ';
+ el += ' ';
+ el += ' ';
+ el += ' ';
+ el += ' ';
+ el += ' ';
+ if (page.layout == 'post' && page.cover && page.cover.length > 0) {
+ el += ' ';
+ }
+ return el;
+}
+function genrateTwitter() {
+ if (theme.head.twitter == undefined || theme.head.twitter.length == 0 || theme.head.twitter == false) {
+ return '';
+ }
+ var el = '';
+ if (theme.head.twitter != 'true') {
+ el += ' ';
+ }
+ if (page.layout == 'post' && page.cover && page.cover.length > 0) {
+ el += ' ';
+ el += ' ';
+ }
+ return el;
+}
+%>
+
+
+ <%- genrateRobots() %>
+
+
+
+
+
+
+
+
+
+
+
+ <%- getTitle() %>
+ <%- genrateKeywords() %>
+ <%- genrateDescription() %>
+ <%- genrateOpenGraph() %>
+ <%- genrateTwitter() %>
+
+
+
+ <% if (config.feed && config.feed.path) { %>
+ <%- feed_tag(config.feed.path, {title: config.title}) %>
+ <% } %>
+
+ <% if (theme.stellar.cdn_css) { %>
+ <%- css(theme.stellar.cdn_css) %>
+ <% } else { %>
+ <%- css('/css/main.css') %>
+ <% } %>
+
+ <% if (config.inject && config.inject.head){ %>
+ <% (config.inject.head||[]).forEach(function(item){ %>
+ <%- item %>
+ <% }) %>
+ <% } else if (config.favicon) { %>
+
+ <% } %>
+
diff --git a/layout/_partial/menubtn.ejs b/layout/_partial/menubtn.ejs
new file mode 100644
index 0000000..dff7a33
--- /dev/null
+++ b/layout/_partial/menubtn.ejs
@@ -0,0 +1,5 @@
+
+
+
diff --git a/layout/_partial/navbar/breadcrumb.ejs b/layout/_partial/navbar/breadcrumb.ejs
new file mode 100644
index 0000000..6987283
--- /dev/null
+++ b/layout/_partial/navbar/breadcrumb.ejs
@@ -0,0 +1,47 @@
+<% if (page.breadcrumb != false) { %>
+
+ <% if (page.layout == 'post') { %>
+
+
<%- __('btn.home') %>
+
+
<%- __('btn.blog') %>
+ <% if (page.layout == 'post' && page.categories && page.categories.length > 0) { %>
+
+ <%- list_categories(page.categories, {
+ class: 'cap breadcrumb cat',
+ show_count: false,
+ separator: '
',
+ style: 'none'
+ }) %>
+ <% } %>
+
+
+ <%- date(page.date, config.date_format) %>
+
+ <% } else if (page.layout == 'wiki') { %>
+
+
<%- __('btn.home') %>
+
+
<%- __('btn.wiki') %>
+ <% site.pages.filter(function (p) { %>
+ <% return p.layout == 'index' && p.title && p.wiki && p.wiki.includes(page.wiki) %>
+ <% }).limit(1).each(function(p) { %>
+
+
<%- p.title %>
+ <% }); %>
+ <% site.pages.filter(function (p) { %>
+ <% return p.layout == 'wiki' && p.wiki == page.wiki && !p.order %>
+ <% }).limit(1).each(function(p) { %>
+
+
<%- page.wiki %>
+ <% }); %>
+
+ <% } else { %>
+
+ <% } %>
+
+<% } %>
diff --git a/layout/_partial/navbar/list_post.ejs b/layout/_partial/navbar/list_post.ejs
new file mode 100644
index 0000000..6a93b1d
--- /dev/null
+++ b/layout/_partial/navbar/list_post.ejs
@@ -0,0 +1,27 @@
+
+ <% if (is_home()) { %>
+ <%- __('btn.recent_publish') %>
+ <% } else { %>
+ <%- __('btn.recent_publish') %>
+ <% } %>
+ <% if (page.category) { %>
+ <%- __('btn.category') + __('symbol.colon') + page.category %>
+ <% } else if (page.layout == 'categories') { %>
+ <%- __('btn.categories') %>
+ <% } else { %>
+ <%- __('btn.categories') %>
+ <% } %>
+ <% if (page.tag) { %>
+ <%- __('btn.tag') + __('symbol.colon') + page.tag %>
+ <% } else if (page.layout == 'tags') { %>
+ <%- __('btn.tags') %>
+ <% } else { %>
+ <%- __('btn.tags') %>
+ <% } %>
+
+ <% if (is_archive()) { %>
+ <%- __('btn.archives') %>
+ <% } else { %>
+ <%- __('btn.archives') %>
+ <% } %>
+
diff --git a/layout/_partial/navbar/list_wiki.ejs b/layout/_partial/navbar/list_wiki.ejs
new file mode 100644
index 0000000..d0117d3
--- /dev/null
+++ b/layout/_partial/navbar/list_wiki.ejs
@@ -0,0 +1,26 @@
+
+ <% if (page.layout == 'index' && page.title && page.wiki) { %>
+ <%- __('btn.all_wiki') %>
+ <% } else { %>
+ <%- __('btn.all_wiki') %>
+ <% } %>
+ <%
+ var groups = [];
+ var paths = [];
+ site.pages.filter(function (p) {
+ return p.layout == 'index' && p.title && p.wiki;
+ }).sort('order').each(function(p) {
+ if (groups.includes(p.wiki) == false) {
+ groups.push(p.title);
+ paths.push(p.path);
+ }
+ });
+ %>
+ <% groups.forEach((group, i) => { %>
+ <% if (page.title == group) { %>
+ <%- group %>
+ <% } else { %>
+ <%- group %>
+ <% } %>
+ <% }); %>
+
diff --git a/layout/_partial/plugins/comments/layout.ejs b/layout/_partial/plugins/comments/layout.ejs
new file mode 100644
index 0000000..94ab05b
--- /dev/null
+++ b/layout/_partial/plugins/comments/layout.ejs
@@ -0,0 +1,27 @@
+<%
+var loadComment = false;
+if (theme.comments.service && theme.comments.service.length > 0) {
+ if (page.comments == undefined || page.comments != false) {
+ loadComment = true;
+ }
+}
+// 合并项目评论
+if (loadComment && page.layout == 'wiki' && page.wiki) {
+ if (page.comment_id == undefined) {
+ if (theme.comments.service == 'utterances') {
+ // 使用项目名作为评论issue标题并共享评论数据
+ page.comment_id = page.wiki;
+ }
+ }
+}
+%>
+<% if (loadComment) { %>
+
+<% } %>
diff --git a/layout/_partial/plugins/comments/script.ejs b/layout/_partial/plugins/comments/script.ejs
new file mode 100644
index 0000000..b22ff7e
--- /dev/null
+++ b/layout/_partial/plugins/comments/script.ejs
@@ -0,0 +1,3 @@
+<% if (theme.comments.service && theme.comments.service.length > 0) { %>
+ <%- partial(theme.comments.service + '/script') %>
+<% } %>
diff --git a/layout/_partial/plugins/comments/utterances/layout.ejs b/layout/_partial/plugins/comments/utterances/layout.ejs
new file mode 100644
index 0000000..a447688
--- /dev/null
+++ b/layout/_partial/plugins/comments/utterances/layout.ejs
@@ -0,0 +1 @@
+
diff --git a/layout/_partial/plugins/comments/utterances/script.ejs b/layout/_partial/plugins/comments/utterances/script.ejs
new file mode 100644
index 0000000..0ef8582
--- /dev/null
+++ b/layout/_partial/plugins/comments/utterances/script.ejs
@@ -0,0 +1,29 @@
+
diff --git a/layout/_partial/post_list/paginator.ejs b/layout/_partial/post_list/paginator.ejs
new file mode 100644
index 0000000..6982cdb
--- /dev/null
+++ b/layout/_partial/post_list/paginator.ejs
@@ -0,0 +1,13 @@
+<% if (is_home() && page.total > 1) { %>
+
+
+
+
+
+ <%- page.current %> / <%- page.total %>
+
+
+
+
+
+<% } %>
diff --git a/layout/_partial/post_list/post_card.ejs b/layout/_partial/post_list/post_card.ejs
new file mode 100755
index 0000000..7761503
--- /dev/null
+++ b/layout/_partial/post_list/post_card.ejs
@@ -0,0 +1,59 @@
+<%
+let showCat = false;
+if (post.categories && post.categories.length > 0) {
+ showCat = true;
+}
+%>
+<% if (post.cover) { %>
+
+ <% if (theme.plugins.lazyload && theme.plugins.lazyload.enable) { %>
+ <% if (theme.plugins.lazyload.loadingImg) { %>
+
+ <% } else { %>
+
+ <% } %>
+ <% } else { %>
+
+ <% } %>
+
+<% } %>
+
+ <%- (post.title || post.seo_title) ? (post.title || post.seo_title) : date(post.date, config.date_format) %>
+
+
+ <% if (post.excerpt) { %>
+
<%- strip_html(post.excerpt) %>
+ <% } else if (post.description) { %>
+
<%- post.description %>
+ <% } else if (post.content && theme.article.auto_excerpt > 0) { %>
+
<%- truncate(strip_html(post.content), {length: theme.article.auto_excerpt}) %>
+ <% } %>
+
+
+
+ <%= date(post.date, config.date_format) %>
+
+ <% if (showCat) { %>
+ <% if (post.layout == 'post' && post.categories && post.categories.length > 0) { %>
+ <%
+ var cats = [];
+ if (post.categories) {
+ post.categories.forEach((cat, i) => {
+ cats.push(cat.name);
+ });
+ }
+ function layoutCats() {
+ if (cats.length > 0) {
+ var ret = '';
+ ret += cats.shift()
+ ret += ' ';
+ return ret;
+ } else {
+ return '';
+ }
+ }
+ %>
+ <%- layoutCats() %>
+ <% } %>
+ <% } %>
+
diff --git a/layout/_partial/post_list/wiki_card.ejs b/layout/_partial/post_list/wiki_card.ejs
new file mode 100644
index 0000000..d3c72ad
--- /dev/null
+++ b/layout/_partial/post_list/wiki_card.ejs
@@ -0,0 +1,41 @@
+<% if (post.logo) { %>
+ <%
+ var arr = post.logo.split(' ');
+ var height = '';
+ var src = '';
+ arr.forEach(function(item) {
+ var px = item.match(/[0-9]*px/i);
+ if (px && px.length > 0) {
+ px = px[0];
+ height = px;
+ } else {
+ src = item;
+ }
+ });
+ %>
+
+ <% if (height.length > 0) { %>
+
+ <% } else { %>
+
+ <% } %>
+
+<% } %>
+
+
+ <% site.pages.filter(function (p) { %>
+ <% return p.layout == 'index' && p.title && p.wiki && p.wiki.includes(post.wiki) %>
+ <% }).limit(1).each(function(p) { %>
+ <%- p.title %>
+ <% }); %>
+
+
<%- post.wiki || post.title %>
+ <% if (post.description) { %>
+
<%- post.description %>
+ <% } else if (post.excerpt) { %>
+ <%- strip_html(post.excerpt) %>
+ <% } %>
+
+ 了解详情
+
+
diff --git a/layout/_partial/scripts/global.ejs b/layout/_partial/scripts/global.ejs
new file mode 100644
index 0000000..c2d0c0d
--- /dev/null
+++ b/layout/_partial/scripts/global.ejs
@@ -0,0 +1,72 @@
+
diff --git a/layout/_partial/scripts/index.ejs b/layout/_partial/scripts/index.ejs
new file mode 100644
index 0000000..a7cdbf5
--- /dev/null
+++ b/layout/_partial/scripts/index.ejs
@@ -0,0 +1,30 @@
+
+<%- partial('global') %>
+<% if (theme.stellar.cdn_js) { %>
+ <%- js({src: theme.stellar.cdn_js, async: true}) %>
+<% } else { %>
+ <%- js({src: 'js/main.js', async: true}) %>
+<% } %>
+<%- js({src: theme.plugins.jquery || 'https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js'}) %>
+
+
+
+<%- partial('lazyload') %>
+<%- partial('preload') %>
+<%- partial('swiper') %>
+
+<%- partial('scrollreveal') %>
+
+
+<%- partial('../plugins/comments/script') %>
+
+
+<% if (config.inject && config.inject.script && config.inject.script.length > 0) { %>
+ <% config.inject.script.forEach(function(js) { %>
+ <%- js %>
+ <% }) %>
+<% } %>
+
+
+
+<%- partial('issues') %>
diff --git a/layout/_partial/scripts/issues.ejs b/layout/_partial/scripts/issues.ejs
new file mode 100644
index 0000000..f78d042
--- /dev/null
+++ b/layout/_partial/scripts/issues.ejs
@@ -0,0 +1,11 @@
+
diff --git a/layout/_partial/scripts/lazyload.ejs b/layout/_partial/scripts/lazyload.ejs
new file mode 100644
index 0000000..48bd52a
--- /dev/null
+++ b/layout/_partial/scripts/lazyload.ejs
@@ -0,0 +1,25 @@
+<% if (theme.plugins.lazyload && theme.plugins.lazyload.enable) { %>
+
+
+
+<% } %>
diff --git a/layout/_partial/scripts/preload.ejs b/layout/_partial/scripts/preload.ejs
new file mode 100644
index 0000000..5db5783
--- /dev/null
+++ b/layout/_partial/scripts/preload.ejs
@@ -0,0 +1,16 @@
+<% if (theme.plugins.preload.enable && theme.plugins.preload.service) { %>
+ <% let preload = theme.plugins.preload; %>
+ <% if (preload.service == 'instant_page') { %>
+
+ <% } else if (preload.service == 'flying_pages') { %>
+
+
+ <% } %>
+<% } %>
diff --git a/layout/_partial/scripts/scrollreveal.ejs b/layout/_partial/scripts/scrollreveal.ejs
new file mode 100644
index 0000000..6760c34
--- /dev/null
+++ b/layout/_partial/scripts/scrollreveal.ejs
@@ -0,0 +1,12 @@
+<% if (theme.plugins.scrollreveal.enable && theme.plugins.scrollreveal.js) { %>
+ <%- js(theme.plugins.scrollreveal.js) %>
+
+<% } %>
diff --git a/layout/_partial/scripts/swiper.ejs b/layout/_partial/scripts/swiper.ejs
new file mode 100644
index 0000000..e9a2504
--- /dev/null
+++ b/layout/_partial/scripts/swiper.ejs
@@ -0,0 +1,29 @@
+
diff --git a/layout/_partial/sidebar/header.ejs b/layout/_partial/sidebar/header.ejs
new file mode 100644
index 0000000..2e2c927
--- /dev/null
+++ b/layout/_partial/sidebar/header.ejs
@@ -0,0 +1,17 @@
+
diff --git a/layout/_partial/sidebar/index.ejs b/layout/_partial/sidebar/index.ejs
new file mode 100755
index 0000000..3b094fb
--- /dev/null
+++ b/layout/_partial/sidebar/index.ejs
@@ -0,0 +1,38 @@
+<%
+// 默认组件
+if (page.sidebar == undefined) {
+ if (page.layout == 'post' && page.content) {
+ page.sidebar = ['toc', 'recent'];
+ } else if (page.layout == 'wiki' && page.content) {
+ page.sidebar = ['toc', 'wiki_more'];
+ } else {
+ page.sidebar = ['welcome', 'recent'];
+ }
+}
+function layoutFooterDiv() {
+ if (theme.footer.social && theme.footer.social.length > 0) {
+ var el = '';
+ return el;
+ } else {
+ return '';
+ }
+}
+%>
+
+ <%- partial('header') %>
+
+ <% page.sidebar.forEach(function(widget){ %>
+ <% if (widget in theme.sidebar.widgets) { %>
+ <% let w = theme.sidebar.widgets[widget]; %>
+ <%- partial('widgets/' + w.layout, {item: w}) %>
+ <% } %>
+ <% }) %>
+
+ <%- layoutFooterDiv() %>
+
diff --git a/layout/_partial/sidebar/logo.ejs b/layout/_partial/sidebar/logo.ejs
new file mode 100644
index 0000000..8c1040b
--- /dev/null
+++ b/layout/_partial/sidebar/logo.ejs
@@ -0,0 +1,6 @@
+
+ <% if (theme.sidebar.logo.avatar != false) { %>
+
+ <% } %>
+ <%- theme.sidebar.logo.title || config.title %>
+
diff --git a/layout/_partial/sidebar/widgets/markdown.ejs b/layout/_partial/sidebar/widgets/markdown.ejs
new file mode 100644
index 0000000..3f3736e
--- /dev/null
+++ b/layout/_partial/sidebar/widgets/markdown.ejs
@@ -0,0 +1,10 @@
+
diff --git a/layout/_partial/sidebar/widgets/recent.ejs b/layout/_partial/sidebar/widgets/recent.ejs
new file mode 100644
index 0000000..efe0f16
--- /dev/null
+++ b/layout/_partial/sidebar/widgets/recent.ejs
@@ -0,0 +1,30 @@
+
diff --git a/layout/_partial/sidebar/widgets/toc.ejs b/layout/_partial/sidebar/widgets/toc.ejs
new file mode 100644
index 0000000..d7c0dfe
--- /dev/null
+++ b/layout/_partial/sidebar/widgets/toc.ejs
@@ -0,0 +1,77 @@
+<%
+var enable = false;
+if (page.layout == 'post') {
+ if (page.content && toc(page.content).length > 0) {
+ enable = true;
+ }
+} else if (page.layout == 'wiki' || page.sidebar.includes('toc') == true) {
+ enable = true;
+}
+
+function layout_toc() {
+ if (toc(page.content).length > 0) {
+ return toc(page.content, {
+ list_number: item.list_number,
+ min_depth: item.min_depth,
+ max_depth: item.max_depth
+ });
+ }
+}
+ %>
+<% if (enable == true) { %>
+
+<% } %>
diff --git a/layout/_partial/sidebar/widgets/wiki_more.ejs b/layout/_partial/sidebar/widgets/wiki_more.ejs
new file mode 100644
index 0000000..671b770
--- /dev/null
+++ b/layout/_partial/sidebar/widgets/wiki_more.ejs
@@ -0,0 +1,28 @@
+<% if (page.layout == 'wiki') { %>
+ <% site.pages.filter(function (p) { %>
+ <% return p.layout == 'index' && p.title && p.wiki && p.wiki.includes(page.wiki) %>
+ <% }).limit(1).each(function(current_group) { %>
+ <% if (current_group.wiki.length > 1) { %>
+
+ <% } %>
+ <% }); %>
+<% } %>
diff --git a/layout/archive.ejs b/layout/archive.ejs
new file mode 100755
index 0000000..2e8d7d7
--- /dev/null
+++ b/layout/archive.ejs
@@ -0,0 +1,43 @@
+<%
+if (page.class == undefined) {
+ page.class = 'post';
+}
+%>
+<% if (page.posts && (is_category() || is_tag())) { %>
+ <%- partial('index') %>
+<% } else { %>
+ <% page.title = __('btn.archives'); %>
+ <%- partial('_partial/navbar/list_post') %>
+
+ <% var years = []; %>
+ <% site.posts.sort('date', -1).each(function(post) { %>
+ <% post.year = date(post.date, 'YYYY'); %>
+ <% if (post.year && (years.includes(post.year) == false) && (post.title || post.date)) { %>
+ <% years.push(post.year); %>
+ <% } %>
+ <% }); %>
+
<%- __('page.archives', years.length, site.posts.length) %>
+ <% years.forEach((year, i) => { %>
+
+ <%= year %>
+ <% site.posts.sort('date', -1).filter(function (post) { %>
+ <% post.year = date(post.date, 'YYYY'); %>
+ <% return post.year == year; %>
+ <% }).each(function(post) { %>
+
+ <% }); %>
+
+ <% }); %>
+
+<% } %>
diff --git a/layout/categories.ejs b/layout/categories.ejs
new file mode 100755
index 0000000..fb74330
--- /dev/null
+++ b/layout/categories.ejs
@@ -0,0 +1,23 @@
+<%
+if (page.class == undefined) {
+ page.class = 'post';
+}
+%>
+<% if (site.categories.length) { %>
+ <% page.title = __('btn.categories'); %>
+ <% page.layout = 'categories'; %>
+ <%- partial('_partial/navbar/list_post') %>
+
+
<%- __('page.categories', site.categories.length) %>
+
+ <% site.categories.sort('path').each(function(category){ %>
+
+ <% }) %>
+
+
+<% } %>
diff --git a/layout/index.ejs b/layout/index.ejs
new file mode 100755
index 0000000..510578f
--- /dev/null
+++ b/layout/index.ejs
@@ -0,0 +1,63 @@
+<%
+if (page.class == undefined) {
+ if (page.layout == 'index' && page.title && page.wiki) {
+ page.class = 'wiki';
+ } else {
+ page.class = 'post';
+ }
+}
+%>
+<% if (page.class == 'post') { %>
+ <%- partial('_partial/navbar/list_post') %>
+ <% if (page.posts) { %>
+
+ <% page.posts.each(function(post){ %>
+
+
+ <%- partial('_partial/post_list/post_card', {post: post}) %>
+
+
+ <% }) %>
+ <%- partial('_partial/post_list/paginator') %>
+
+ <% } %>
+<% } else if (page.class == 'wiki') { %>
+ <% function outputExcerpt(post) { %>
+
+
+ <%- partial('_partial/post_list/wiki_card', {post: post}) %>
+
+
+ <% } %>
+ <%- partial('_partial/navbar/list_wiki') %>
+ <% if (page.title && page.wiki) { %>
+
+ <% page.wiki.forEach((wiki, i) => { %>
+ <% site.pages.filter(function (p) { %>
+ <% return p.layout == 'wiki' && p.wiki == wiki && p.description; %>
+ <% }).limit(1).each(function(post) { %>
+ <%- outputExcerpt(post) %>
+ <% }); %>
+ <% }); %>
+
+ <% } else { %>
+
+ <%
+ var wikis = [];
+ wikis = site.pages.filter(function (p) {
+ if (p.layout == 'wiki' && p.wiki && p.description) {
+ if (p.order == undefined) {
+ p.order = 0;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }).sort('order');
+ %>
+ <% wikis.forEach(function(post) { %>
+ <%- outputExcerpt(post) %>
+ <% }); %>
+
+ <% } %>
+<% } %>
diff --git a/layout/layout.ejs b/layout/layout.ejs
new file mode 100755
index 0000000..144456d
--- /dev/null
+++ b/layout/layout.ejs
@@ -0,0 +1,21 @@
+
+
+<%- partial('_partial/head') %>
+
+ <%- partial('_partial/article/wiki_cover') %>
+
+
+ <%- partial('_partial/sidebar/index') %>
+
+
+ <%- partial('_partial/sidebar/header') %>
+ <%- body %>
+
+ <%- partial('_partial/menubtn') %>
+
+
+ <%- partial('_partial/scripts/index') %>
+
+
diff --git a/layout/page.ejs b/layout/page.ejs
new file mode 100755
index 0000000..d20aecd
--- /dev/null
+++ b/layout/page.ejs
@@ -0,0 +1,12 @@
+<% if ((page.h1 && page.h1.length > 0) || (page.content && page.content.length > 0)) { %>
+ <%- partial('_partial/navbar/breadcrumb') %>
+
+ <% if (page.h1 && page.h1.length > 0) { %>
+ <%- page.h1 %>
+ <% } %>
+ <% if (page.content && page.content.length > 0) { %>
+ <%- page.content %>
+ <% } %>
+
+<% } %>
+<%- partial('_partial/plugins/comments/layout') %>
diff --git a/layout/post.ejs b/layout/post.ejs
new file mode 100755
index 0000000..6e0228e
--- /dev/null
+++ b/layout/post.ejs
@@ -0,0 +1,15 @@
+<%
+if (page.class == undefined) {
+ page.class = 'post';
+}
+%>
+<% let post = page; %>
+<%- partial('_partial/navbar/breadcrumb') %>
+
+<%- post.h1 || post.title %>
+<%- post.content %>
+
+<%- partial('_partial/article/references') %>
+<%- partial('_partial/article/read_next') %>
+<%- partial('_partial/article/related_posts') %>
+<%- partial('_partial/plugins/comments/layout') %>
diff --git a/layout/tags.ejs b/layout/tags.ejs
new file mode 100755
index 0000000..1ad3c11
--- /dev/null
+++ b/layout/tags.ejs
@@ -0,0 +1,21 @@
+<%
+if (page.class == undefined) {
+ page.class = 'post';
+}
+%>
+<% if (site.tags.length) { %>
+ <% page.title = __('btn.tags'); %>
+ <% page.layout = 'tags'; %>
+ <%- partial('_partial/navbar/list_post') %>
+
+
<%- __('page.tags', site.tags.length) %>
+
+ <% site.tags.sort('length', -1).each(function(tag){ %>
+
+ <%= tag.name %>
+ x<%- tag.length %>
+
+ <% }) %>
+
+
+<% } %>
diff --git a/layout/wiki.ejs b/layout/wiki.ejs
new file mode 100755
index 0000000..fe4b1fe
--- /dev/null
+++ b/layout/wiki.ejs
@@ -0,0 +1,22 @@
+<%
+if (page.class == undefined) {
+ page.class = 'wiki';
+}
+if (page.layout == undefined) {
+ page.layout = 'index';
+}
+if (page.title == undefined) {
+ page.title = 'Wiki';
+}
+%>
+<% if (page.layout == 'index') { %>
+ <%- partial('index') %>
+<% } else { %>
+ <%- partial('_partial/navbar/breadcrumb') %>
+
+ <%- page.h1 || page.title %>
+ <%- page.content %>
+
+ <%- partial('_partial/article/read_next') %>
+ <%- partial('_partial/plugins/comments/layout') %>
+<% } %>
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..3e11497
--- /dev/null
+++ b/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "hexo-theme-stellar",
+ "version": "1.0.0-rc.1",
+ "description": "Elegant and powerful theme for Hexo.",
+ "main": "package.json",
+ "scripts": {
+ "test": "echo test"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/xaoxuu/hexo-theme-stellar.git"
+ },
+ "keywords": [
+ "hexo",
+ "theme",
+ "stellar"
+ ],
+ "author": "xaoxuu (https://github.com/xaoxuu/)",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/xaoxuu/hexo-theme-stellar/issues/"
+ },
+ "homepage": "https://xaoxuu.com/wiki/stellar/",
+ "devDependencies": {
+ }
+}
diff --git a/publish.sh b/publish.sh
new file mode 100644
index 0000000..9c233a9
--- /dev/null
+++ b/publish.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+#
+# https://xaoxuu.com/wiki/stellar
+#
+# 只有 rc 阶段的测试版本和正式版本发布到 npm
+#
+# 1. 输入要发布的版本号
+# 2. 修改主题 _config.yml 中的 stellar.version
+# 3. 修改主题 package.json 中的 version
+# 4. 提交 commit
+
+# 版本号 例如 1.0.0-rc.1
+VERSION=$1
+
+# 替换版本号
+function prepare() {
+ text="'"${VERSION}"'"" # This is theme's version."
+ sed -i "" "s/^ version:\([^\"]\{1,\}\)/ version: ${text}/g" '_config.yml'
+ sed -i "" "s/^ \"version\":\([^,]\{1,\}\)/ \"version\": \"${VERSION}\"/g" 'package.json'
+}
+
+# 提交
+function commit() {
+ msg="release: ${VERSION}"
+
+ printf "\n\n> \033[32m%s\033[0m" 'git add --all'
+ printf "\n"
+ git add --all
+
+ printf "\n\n> \033[32m%s\033[0m" 'git commit -m'
+ printf " \033[35m%s\033[0m" ${msg}
+ printf "\n"
+ git commit -m "${msg}"
+
+ printf "\n\n> \033[32m%s\033[0m" 'git push origin'
+ printf "\n"
+ git push origin
+
+ # done
+ printf "\n\n> \033[32m%s\033[0m\n" 'Congratulations!'
+}
+
+
+while :
+do
+ case $VERSION in
+ '')
+ read -p "请输入要发布的版本号: " VERSION
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+prepare && commit
diff --git a/scripts/events/index.js b/scripts/events/index.js
new file mode 100644
index 0000000..7daf1b6
--- /dev/null
+++ b/scripts/events/index.js
@@ -0,0 +1,18 @@
+/* global hexo */
+
+'use strict';
+
+hexo.on('generateBefore', () => {
+ // Merge config.
+ require('./lib/config')(hexo);
+});
+
+hexo.on('ready', () => {
+ const { version } = require('../../package.json');
+ hexo.log.info(`
+===================================================
+Stellar ${version}
+Docs: https://xaoxuu.com/wiki/stellar/
+Repo: https://github.com/xaoxuu/hexo-theme-stellar/
+===================================================`);
+});
\ No newline at end of file
diff --git a/scripts/events/lib/config.js b/scripts/events/lib/config.js
new file mode 100644
index 0000000..ee52362
--- /dev/null
+++ b/scripts/events/lib/config.js
@@ -0,0 +1,41 @@
+/**
+ * 部分代码借鉴自 NexT:
+ * https://github.com/next-theme/hexo-theme-next/blob/master/scripts/events/lib/config.js
+ */
+
+'use strict';
+
+module.exports = hexo => {
+
+ const { cache, language_switcher } = hexo.theme.config;
+ const warning = function(...args) {
+ hexo.log.warn(`Since ${args[0]} is turned on, the ${args[1]} is disabled to avoid potential hazards.`);
+ };
+
+ if (cache && cache.enable && language_switcher) {
+ warning('language_switcher', 'caching');
+ cache.enable = false;
+ }
+
+ if (cache && cache.enable && hexo.config.relative_link) {
+ warning('caching', '`relative_link` option in Hexo `_config.yml`');
+ hexo.config.relative_link = false;
+ }
+ hexo.config.meta_generator = false;
+
+ // merge data
+ const data = hexo.locals.get('data');
+ if (data.widgets) {
+ for (let id of Object.keys(data.widgets)) {
+ hexo.theme.config.sidebar.widgets[id] = data.widgets[id];
+ }
+ }
+
+
+ // default widgets
+ if (hexo.theme.config.sidebar.widgets.wiki_more == undefined) {
+ hexo.theme.config.sidebar.widgets.wiki_more = {layout: 'wiki_more'};
+ }
+
+
+};
diff --git a/scripts/filters/index.js b/scripts/filters/index.js
new file mode 100644
index 0000000..2489935
--- /dev/null
+++ b/scripts/filters/index.js
@@ -0,0 +1,5 @@
+'use strict';
+
+
+hexo.extend.filter.register('after_post_render', require('./lib/lazyload').processPost);
+hexo.extend.filter.register('after_render:html', require('./lib/lazyload').processSite);
diff --git a/scripts/filters/lib/lazyload.js b/scripts/filters/lib/lazyload.js
new file mode 100644
index 0000000..6b0926c
--- /dev/null
+++ b/scripts/filters/lib/lazyload.js
@@ -0,0 +1,67 @@
+'use strict';
+
+const fs = require('hexo-fs');
+
+function lazyProcess(htmlContent, target) {
+ const cfg = this.theme.config.plugins.lazyload;
+ if (cfg == undefined || cfg.enable != true) {
+ return htmlContent;
+ }
+ if (cfg.onlypost == true) {
+ if (target != 'post') {
+ return htmlContent;
+ }
+ }
+ const loadingImg = cfg.loadingImg;
+ return htmlContent.replace(/ /gi, function(str, p1, p2) {
+ // might be duplicate
+ if (/data-srcset/gi.test(str)) {
+ return str;
+ }
+ if (/src="data:image(.*?)/gi.test(str)) {
+ return str;
+ }
+ if (/no-lazy/gi.test(str)) {
+ return str;
+ }
+ let cls = '';
+ if (str.indexOf('class=') > -1) {
+ cls = str.substring(str.indexOf('class='));
+ if (cls.length > 7) {
+ const c = cls.substring(6, 7);
+ cls = cls.split(c);
+ if (cls.length > 1) {
+ cls = cls[0] + '"' + cls[1] + '"';
+ }
+ }
+ }
+ let result = str;
+ let newCls = '';
+ if (cls.length > 0 && result.includes('class=')) {
+ newCls = cls.replace(/(class=|[\"]*)/g, '') + ' ';
+ }
+ const oldCls = newCls.trim();
+ if (loadingImg) {
+ newCls += 'lazyload placeholder';
+ } else {
+ newCls += 'lazyload';
+ }
+ if (cls.length > 0) {
+ result = result.replace('"' + oldCls + '"', '"' + newCls + '"');
+ }
+ if (loadingImg) {
+ return result.replace(p2, p2 + '" class="lazyload placeholder" ' + 'data-srcset="' + p2 + '" srcset="' + loadingImg);
+ }
+ return result.replace(p2, p2 + '" class="lazyload" ' + 'data-srcset="' + p2 + '" srcset="' + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAADa6r/EAAAAC0lEQVQIHWNgAAIAAAUAAY27m/MAAAAASUVORK5CYII=');
+
+ });
+}
+
+module.exports.processPost = function(data) {
+ data.content = lazyProcess.call(this, data.content, 'post');
+ return data;
+};
+
+module.exports.processSite = function(htmlContent) {
+ return lazyProcess.call(this, htmlContent, 'site');
+};
diff --git a/scripts/generators/404.js b/scripts/generators/404.js
new file mode 100644
index 0000000..942532a
--- /dev/null
+++ b/scripts/generators/404.js
@@ -0,0 +1,10 @@
+/**
+ * 404 v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ */
+
+hexo.extend.generator.register('404', function (locals) {
+ return {
+ path: '/404.html',
+ layout: ['404']
+ }
+});
\ No newline at end of file
diff --git a/scripts/generators/categories.js b/scripts/generators/categories.js
new file mode 100644
index 0000000..410a3fa
--- /dev/null
+++ b/scripts/generators/categories.js
@@ -0,0 +1,11 @@
+/**
+ * categories v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ */
+
+hexo.extend.generator.register('categories', function (locals) {
+ return {
+ path: hexo.config.category_dir + '/index.html',
+ data: locals.posts,
+ layout: ['categories']
+ }
+});
\ No newline at end of file
diff --git a/scripts/generators/tags.js b/scripts/generators/tags.js
new file mode 100644
index 0000000..09eb19c
--- /dev/null
+++ b/scripts/generators/tags.js
@@ -0,0 +1,11 @@
+/**
+ * tags v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ */
+
+ hexo.extend.generator.register('tags', function (locals) {
+ return {
+ path: hexo.config.tag_dir + '/index.html',
+ data: locals.posts,
+ layout: ['tags']
+ }
+});
\ No newline at end of file
diff --git a/scripts/generators/wiki.js b/scripts/generators/wiki.js
new file mode 100644
index 0000000..27aef64
--- /dev/null
+++ b/scripts/generators/wiki.js
@@ -0,0 +1,11 @@
+/**
+ * wiki v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ */
+
+hexo.extend.generator.register('wiki', function (locals) {
+ return {
+ path: (hexo.config.wiki_dir || 'wiki') + '/index.html',
+ data: locals.posts,
+ layout: ['wiki']
+ }
+});
\ No newline at end of file
diff --git a/scripts/helpers/related_posts.js b/scripts/helpers/related_posts.js
new file mode 100644
index 0000000..ea6cfe8
--- /dev/null
+++ b/scripts/helpers/related_posts.js
@@ -0,0 +1,54 @@
+/**
+ * https://github.com/tea3/hexo-related-popular-posts/wiki/More-Settings#customize-html
+ */
+
+'use strict';
+var util = require('hexo-util');
+
+// Examples of helper
+hexo.extend.helper.register('popular_posts_wrapper', function(args){
+ if (!args || !args.json || args.json.length == 0) return "";
+ const cfg = hexo.theme.config.article.related_posts;
+ if (cfg.enable != true) return;
+ var returnHTML = "";
+ var div = `
+ ';
+ return div;
+});
diff --git a/scripts/tags/about.js b/scripts/tags/about.js
new file mode 100644
index 0000000..bb63cd9
--- /dev/null
+++ b/scripts/tags/about.js
@@ -0,0 +1,46 @@
+/**
+ * about.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% about [avatar:xxx] [height:80px] %}
+ * title / body
+ * {% endabout %}
+ */
+
+'use strict';
+
+const { ArgsMap } = require('./utils');
+
+hexo.extend.tag.register('about', function(args, content) {
+ args = ArgsMap(args, ['avatar', 'height']);
+ var rows = hexo.render.renderSync({text: content, engine: 'markdown'}).split('\n');
+ var el = '';
+ // wrapper
+ el += '';
+ el += '';
+
+ // content
+ el += '
';
+ el += rows.join('');
+ el += '
';
+
+ el += '
';
+ return el;
+}, {ends: true});
diff --git a/scripts/tags/checkbox.js b/scripts/tags/checkbox.js
new file mode 100644
index 0000000..662af11
--- /dev/null
+++ b/scripts/tags/checkbox.js
@@ -0,0 +1,40 @@
+/**
+ * checkbox.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * radio.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% checkbox [checked:false] [color:cyan] [symbol:plus/minus/times] text %}
+ * {% radio [checked:false] [color:cyan] text %}
+ */
+
+'use strict';
+
+const { ArgsMap, ArgsJoinTags } = require('./utils');
+
+function layoutDiv(args, type) {
+ args = ArgsMap(args, ['color', 'checked', 'symbol'], ['text']);
+ var el = '';
+ // div
+ el += '';
+ // input
+ el += ' ';
+ // text
+ el += '' + args.text + ' ';
+ // div
+ el += '
';
+ return el;
+}
+
+hexo.extend.tag.register('checkbox', function(args) {
+ return layoutDiv(args, 'checkbox');
+});
+
+hexo.extend.tag.register('radio', function(args) {
+ return layoutDiv(args, 'radio');
+});
diff --git a/scripts/tags/folding.js b/scripts/tags/folding.js
new file mode 100644
index 0000000..59df2a5
--- /dev/null
+++ b/scripts/tags/folding.js
@@ -0,0 +1,32 @@
+/**
+ * folding.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% folding [color:yellow] [codeblock:false] [open:false] title %}
+ * body
+ * {% endtable %}
+ */
+
+'use strict';
+
+const { ArgsMap, ArgsJoinTags } = require('./utils');
+
+hexo.extend.tag.register('folding', function(args, content) {
+ args = ArgsMap(args, ['color', 'codeblock', 'open'], ['title']);
+ var el = '';
+ // header
+ el += '';
+ // summary
+ el += '' + args.title + ' ';
+ // content
+ el += '';
+ el += hexo.render.renderSync({text: content, engine: 'markdown'}).split('\n').join('');
+ el += '
';
+
+ return el;
+}, {ends: true});
diff --git a/scripts/tags/frame.js b/scripts/tags/frame.js
new file mode 100644
index 0000000..b230bed
--- /dev/null
+++ b/scripts/tags/frame.js
@@ -0,0 +1,68 @@
+/**
+ * frame.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% frame iphone11 [img:src] [video:url] [focus:top/bottom] [alt] %}
+ */
+
+'use strict';
+
+const { ArgsMap } = require('./utils');
+
+hexo.extend.tag.register('frame', function(args) {
+ args = ArgsMap(args, ['focus', 'img', 'video'], ['device', 'alt']);
+ const img = args.img || '';
+ const video = args.video || '';
+ const device = args.device || '';
+ const focus = args.focus || '';
+ const alt = args.alt || '';
+ if ((img.length == 0 && video.length == 0) || device.length == 0) {
+ return;
+ }
+ var el = '';
+ function imgTag(url, alt) {
+ let i = '';
+ i += ' 0) {
+ i += ' alt="' + alt + '"';
+ }
+ i += '/>';
+ return i;
+ }
+ if (video.length > 0) {
+ el += '';
+ el += '
0) {
+ el += 'focus="' + focus + '">';
+ } else {
+ el += '>';
+ }
+ el += '
0) {
+ el += ' poster="' + img + '"';
+ }
+ el += ' playsinline="" muted="" loop="" autoplay="" preload="metadata">';
+ el += '';
+ el += ' ';
+
+ el += '
';
+ el += '
';
+ el += '
';
+ } else if (img.length > 0) {
+ el += '';
+ el += '
0) {
+ el += 'focus="' + focus + '">';
+ } else {
+ el += '>';
+ }
+ el += imgTag(img, alt);
+ el += '
';
+ el += '
';
+ if (alt.length > 0) {
+ el += '
' + alt + ' ';
+ }
+ el += '
';
+ }
+ return el;
+});
diff --git a/scripts/tags/friends.js b/scripts/tags/friends.js
new file mode 100644
index 0000000..923d12f
--- /dev/null
+++ b/scripts/tags/friends.js
@@ -0,0 +1,66 @@
+/**
+ * friends.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% friends [style:rect] [group:name] %}
+ */
+
+'use strict';
+
+const { ArgsMap } = require('./utils');
+
+hexo.extend.tag.register('friends', function(args) {
+ args = ArgsMap(args, ['style', 'group']);
+ var friends = hexo.locals.get('data').friends;
+ if (friends == undefined) {
+ return;
+ }
+ var el = '';
+ function groupHeader(group) {
+ var header = '';
+ return header;
+ }
+ function cell(friend) {
+ if (friend.url && friend.title) {
+ var cell = '
'
+ return cell;
+ } else {
+ return '';
+ }
+ }
+ for (let groupId of Object.keys(friends)) {
+ function f() {
+ if (groupId in friends) {
+ let group = friends[groupId];
+ el += groupHeader(group);
+ el += '
';
+ group.items.forEach((friend, i) => {
+ el += cell(friend);
+ });
+ el += '
';
+ }
+ }
+ if (args.group && args.group.length > 0) {
+ if (args.group == groupId) {
+ f();
+ }
+ } else {
+ f();
+ }
+ }
+ el += '
';
+ return el;
+});
diff --git a/scripts/tags/ghcard.js b/scripts/tags/ghcard.js
new file mode 100644
index 0000000..7184b19
--- /dev/null
+++ b/scripts/tags/ghcard.js
@@ -0,0 +1,41 @@
+/**
+ * friends.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% ghcard user/repo [theme:xxx] %} or {% ghcard user %}
+ *
+ * example:
+ * {% ghcard xaoxuu %}
+ * {% ghcard xaoxuu/hexo-theme-stellar %}
+ *
+ * API: https://github.com/anuraghazra/github-readme-stats
+ */
+
+'use strict';
+
+const { ArgsMap, ArgsJoinURLParams } = require('./utils');
+
+hexo.extend.tag.register('ghcard', function(args) {
+ var params = ['show_owner', 'theme', 'title_color', 'text_color', 'icon_color', 'bg_color', 'hide_border', 'cache_seconds', 'locale'];
+ args = ArgsMap(args, params, ['repo']);
+ const path = args.repo;
+ var el = '';
+ el += '';
+ return el;
+});
diff --git a/scripts/tags/image.js b/scripts/tags/image.js
new file mode 100644
index 0000000..4f21d1b
--- /dev/null
+++ b/scripts/tags/image.js
@@ -0,0 +1,66 @@
+/**
+ * image.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% image src [alt] [width:400px] [bg:#eee] [download:true/false/url] %}
+ */
+
+'use strict';
+
+const { ArgsMap } = require('./utils');
+
+hexo.extend.tag.register('image', function(args) {
+ args = ArgsMap(args, ['width', 'height', 'bg', 'download', 'padding'], ['src', 'alt']);
+ var style = '';
+ if (args.width) {
+ style += 'width:' + args.width + ';';
+ }
+ if (args.height) {
+ style += 'height:' + args.height + ';';
+ }
+ if (args.padding) {
+ style += 'padding:' + args.padding + ';';
+ }
+ function img(src, alt, style) {
+ let img = '';
+ img += ' 0) {
+ img += ' style="' + style + '"';
+ }
+ img += '/>';
+ return img;
+ }
+
+ var el = '';
+ // wrap
+ el += '';
+ // bg
+ el += '
0) {
+ el += ' style="background:' + args.bg + '"';
+ }
+ el += '>';
+ el += img(args.src, args.alt, style);
+ el += '
';
+
+ if (args.alt && args.alt.length > 0) {
+ el += '
';
+ }
+
+ el += '
';
+ return el;
+});
diff --git a/scripts/tags/issues.js b/scripts/tags/issues.js
new file mode 100644
index 0000000..59bacbb
--- /dev/null
+++ b/scripts/tags/issues.js
@@ -0,0 +1,32 @@
+/**
+ * issues.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% issues [sites/timeline/friends] api:xxx [group:key=value1,value2,value3] %}
+ *
+ * example:
+ * {% issues sites api:https://api.github.com/repos/volantis-x/examples/issues?sort=updated&state=open&page=1&per_page=100 group:version=版本:^4.0,版本:^3.0,版本:^2.0 %}
+ */
+
+'use strict';
+
+const { ArgsMap } = require('./utils');
+
+hexo.extend.tag.register('issues', function(args) {
+ args = ArgsMap(args, ['api', 'group'], ['type']);
+ // 所有支持的参数
+ let type = args.type || '';
+ let api = args.api || '';
+ let group = args.group || '';
+ if (type.length == 0 || api.length == 0) {
+ return;
+ }
+ // 布局
+ var el = ' 0) {
+ el += 'group="' + group + '"';
+ }
+ el += '>
';
+ return el;
+});
diff --git a/scripts/tags/link.js b/scripts/tags/link.js
new file mode 100644
index 0000000..d9f2c6d
--- /dev/null
+++ b/scripts/tags/link.js
@@ -0,0 +1,32 @@
+/**
+ * link.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% link url title [description] [img:src] %}
+ */
+
+'use strict';
+
+const { ArgsMap } = require('./utils');
+
+hexo.extend.tag.register('link', function(args) {
+ args = ArgsMap(args, ['src'], ['url', 'title', 'description']);
+ var el = '';
+ el += '';
+
+ return el;
+});
diff --git a/scripts/tags/navbar.js b/scripts/tags/navbar.js
new file mode 100644
index 0000000..f97d998
--- /dev/null
+++ b/scripts/tags/navbar.js
@@ -0,0 +1,38 @@
+/**
+ * navbar.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% navbar [markdown link] ... %}
+ *
+ * example:
+ * {% navbar [Home](/) [About](/about/) [Comments](#comments) %}
+ */
+
+'use strict';
+
+hexo.extend.tag.register('navbar', function(args) {
+ if (args.length == 0) {
+ return;
+ }
+ var el = '';
+ function layoutItem(a) {
+ if (a.includes('](')) {
+ // markdown
+ let el = hexo.render.renderSync({text: a, engine: 'markdown'}).split('\n').join('');
+ if (el.length > 8) {
+ el = el.slice(3, el.length-4);
+ }
+ return el;
+ } else {
+ var item = '';
+ item += a;
+ item += ' ';
+ return item;
+ }
+ }
+ args.forEach((item, i) => {
+ el += layoutItem(item);
+ });
+ el += ' ';
+ return el;
+});
diff --git a/scripts/tags/note.js b/scripts/tags/note.js
new file mode 100644
index 0000000..a7258b0
--- /dev/null
+++ b/scripts/tags/note.js
@@ -0,0 +1,54 @@
+/**
+ * note.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * note:
+ * {% note [color:color] [title] content %}
+ *
+ * noteblock:
+ * {% noteblock [color:color] title %}
+ * markdown content
+ * {% endnoteblock %}
+ */
+
+'use strict';
+
+const { ArgsMap } = require('./utils');
+
+function outputNoteBlock(color, title, content) {
+ var el = '';
+ const defaultColor = hexo.theme.config.tag_plugins.note.default_color;
+ if (!color && defaultColor) {
+ color = defaultColor;
+ }
+ // header
+ el += ' 0) {
+ el += ' color="' + color + '"';
+ }
+ el += '>';
+ // title
+ if (title && title.length > 0) {
+ el += '
' + title + '
';
+ }
+ // content
+ el += '
';
+ el += hexo.render.renderSync({text: content, engine: 'markdown'}).split('\n').join('');
+ el += '
';
+
+ return el;
+}
+
+hexo.extend.tag.register('note', function(args) {
+ args = ArgsMap(args, ['color'], ['title', 'content']);
+ if (args.content) {
+ return outputNoteBlock(args.color, args.title, args.content);
+ } else {
+ return outputNoteBlock(args.color, '', args.title);
+ }
+});
+
+hexo.extend.tag.register('noteblock', function(args, content) {
+ args = ArgsMap(args, ['color'], ['title']);
+ return outputNoteBlock(args.color, args.title, content);
+}, {ends: true});
diff --git a/scripts/tags/swiper.js b/scripts/tags/swiper.js
new file mode 100644
index 0000000..97b98f5
--- /dev/null
+++ b/scripts/tags/swiper.js
@@ -0,0 +1,39 @@
+/**
+ * swiper.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
+ *
+ * {% swiper %}
+ * ![img](src)
+ * {% endswiper %}
+ */
+
+'use strict';
+
+const { ArgsMap, ArgsJoinTags } = require('./utils');
+
+hexo.extend.tag.register('swiper', function(args, content) {
+ args = ArgsMap(args, ['width']);
+ var el = '';
+ function slide() {
+ let imgs = hexo.render.renderSync({text: content, engine: 'markdown'});
+ imgs = imgs.match(/ /gi);
+ if (imgs && imgs.length > 0) {
+ imgs.forEach((img, i) => {
+ el += '' + img + '
';
+ });
+ }
+ }
+ el += ' 0) {
+ el += ' ' + ArgsJoinTags(args, 'width').join(' ');
+ }
+ el += '>';
+ el += '
';
+ slide();
+ el += '
';
+ el += '';
+ el += '
';
+ el += '
';
+ el += '
';
+ return el;
+}, {ends: true});
diff --git a/scripts/tags/timeline.js b/scripts/tags/timeline.js
new file mode 100644
index 0000000..c20abaf
--- /dev/null
+++ b/scripts/tags/timeline.js
@@ -0,0 +1,40 @@
+/**
+ * timeline.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ *
+ * {% timeline %}
+ *
+ * {% timenode header1 %}
+ * what happened 1
+ * {% endtimenode %}
+ *
+ * {% timenode header2 %}
+ * what happened 2
+ * {% endtimenode %}
+ *
+ * {% endtimeline %}
+ */
+
+'use strict';
+
+function postTimeline(args, content) {
+ return ``;
+}
+
+function postTimenode(args, content) {
+ args = args.join(' ').split(', ');
+ var header = args[0];
+ return `${hexo.render.renderSync({text: content, engine: 'markdown'}).split('\n').join('')}
`;
+}
+
+hexo.extend.tag.register('timeline', postTimeline, {ends: true});
+
+hexo.extend.tag.register('timenode', postTimenode, {ends: true});
diff --git a/scripts/tags/utils.js b/scripts/tags/utils.js
new file mode 100644
index 0000000..91015db
--- /dev/null
+++ b/scripts/tags/utils.js
@@ -0,0 +1,72 @@
+/**
+ * folding.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
+ */
+
+'use strict';
+
+
+module.exports = {
+ ArgsMap(args, keys, others) {
+ if (Array.isArray(args) == false) {
+ return args;
+ }
+ var map = {others: Array()};
+ args.forEach((arg, i) => {
+ let kv = arg.trim();
+ if (kv.includes('://') && kv.split(':').length == 2) {
+ // 纯 url
+ map.others.push(kv);
+ } else {
+ kv = kv.split(':');
+ if (kv.length > 1) {
+ if (keys.includes(kv[0]) == true) {
+ map[kv.shift()] = kv.join(':');
+ } else {
+ map.others.push(kv.join(':'));
+ }
+ } else if (kv.length == 1) {
+ map.others.push(kv[0]);
+ }
+ }
+ });
+ // 解析不带 key 的参数
+ if (others && others.length > 0 && map.others.length > 0) {
+ if (Array.isArray(others) == false) {
+ others = [others];
+ }
+ others.forEach((arg, i) => {
+ map[arg] = map.others.shift();
+ });
+ // 最后一段合并到最后一个参数中
+ if (map.others.length > 0) {
+ map[others[others.length-1]] += ' ' + map.others.join(' ');
+ map.others = [];
+ }
+ }
+ return map;
+ },
+ ArgsJoinTags(args, keys) {
+ if (Array.isArray(keys) == false) {
+ keys = [keys];
+ }
+ var ret = [];
+ keys.forEach((key, i) => {
+ if (args[key] && args[key].length > 0) {
+ ret.push(key + '="' + args[key] + '"');
+ }
+ });
+ return ret;
+ },
+ ArgsJoinURLParams(args, keys) {
+ if (Array.isArray(keys) == false) {
+ keys = [keys];
+ }
+ var ret = [];
+ keys.forEach((key, i) => {
+ if (args[key] && args[key].length > 0) {
+ ret.push(key + '=' + args[key]);
+ }
+ });
+ return ret.join('&');
+ }
+};
diff --git a/source/css/_custom.styl b/source/css/_custom.styl
new file mode 100644
index 0000000..d62d659
--- /dev/null
+++ b/source/css/_custom.styl
@@ -0,0 +1,74 @@
+@require('_defines/base')
+@require('_defines/const')
+
+// 通用主题色
+$color-theme = #1BCDFC
+$color-link = #2196f3
+$color-button = #1BCDFC
+$color-hover = #ff5722
+$color-inner = #fff
+$color-inlinecode = #D56D28
+$color-cat = #FF7844
+$color-cat-hover = darken($color-cat, 20)
+
+// 浅色页面
+$c-site-bg-light = #f8f8f8
+$c-block-light = #f2f2f2
+$c-title-light = #111
+$c-text-light = #333
+$c-card-light = white
+
+// 深色页面
+$c-site-bg-dark = black
+$c-block-dark = #111
+$c-title-dark = #fff
+$c-text-dark = #fff
+$c-card-dark = #333
+
+
+
+// @font-face
+// font-family: 'Dosis'
+// src: url('https://cdn.jsdelivr.net/gh/volantis-x/cdn-fonts@20.5.30/Dosis/Dosis-Medium.ttf')
+// font-weight: normal
+// font-style: normal
+
+$ff-body = Dosis, -apple-system, "Helvetica Neue", Helvetica, Arial, "WenQuanYi Micro Hei", "Microsoft Yahei", Menlo, Monaco, monospace, courier, sans-serif
+$ff-code = Menlo, Monaco, monospace, courier, sans-serif
+
+$ff-logo = $ff-body
+
+// font size
+$fs-root = 16px
+$fs-h1 = 2rem // 32px
+$fs-h2 = 1.625rem // 26px
+$fs-h3 = 1.375rem // 22px
+$fs-h4 = 1.125rem // 18px
+$fs-h5 = 1rem // 16px
+$fs-h6 = 1rem // 16px
+$fs15 = .9375rem // 15px
+$fs14 = .875rem // 14px
+$fs13 = .8125rem // 13px
+$fs-code = .8125rem // 13px
+$fs12 = .75rem // 13px
+$fs-footnote = .75rem // the smallest (12px)
+
+
+// site layout
+$sidebar = 280px
+
+// max body width
+$layout-max-body-width = 720px
+
+// gap
+$gap = 16px // 必须是 px
+
+// border radius
+$border-card = 12px
+$border-block = 4px
+
+
+// shadow
+$boxshadow-card = 0 1px 2px 0px rgba(0, 0, 0, 0.1)
+$boxshadow-float = 0 4px 8px 0px rgba(0, 0, 0, 0.1)
+$boxshadow-card-float = 0 2px 4px 0px rgba(0, 0, 0, 0.1), 0 4px 8px 0px rgba(0, 0, 0, 0.1), 0 8px 16px 0px rgba(0, 0, 0, 0.1)
diff --git a/source/css/_defines/base.styl b/source/css/_defines/base.styl
new file mode 100644
index 0000000..2833bdd
--- /dev/null
+++ b/source/css/_defines/base.styl
@@ -0,0 +1,43 @@
+
+// 可替代 transition 使用
+transition($op = all, $time = 0.28s, $ease = ease-out)
+ transition: $op $time $ease
+ -moz-transition: $op $time $ease
+ -webkit-transition: $op $time $ease
+ -o-transition: $op $time $ease
+
+// 为1个属性设置动画
+trans1($op, $time = 0.2s)
+ transition: $op $time ease-out
+ -moz-transition: $op $time ease-out
+ -webkit-transition: $op $time ease-out
+ -o-transition: $op $time ease-out
+
+// 为2个属性设置动画
+trans2($op1, $op2)
+ transition: $op1 0.2s ease-out, $op2 0.2s ease-out
+ -moz-transition: $op1 0.2s ease-out, $op2 0.2s ease-out
+ -webkit-transition: $op1 0.2s ease-out, $op2 0.2s ease-out
+ -o-transition: $op1 0.2s ease-out, $op2 0.2s ease-out
+
+
+txt-ellipsis()
+ white-space: nowrap
+ overflow: hidden
+ text-overflow: ellipsis
+
+placeholder(rules)
+ &::-webkit-input-placeholder
+ rules()
+ &:-moz-placeholder
+ rules()
+ &::-moz-placeholder
+ rules()
+ &:-ms-input-placeholder
+ rules()
+
+disable-select()
+ -moz-user-select: none
+ -ms-user-select: none
+ -webkit-user-select: none
+ user-select: none
diff --git a/source/css/_defines/const.styl b/source/css/_defines/const.styl
new file mode 100644
index 0000000..854afb8
--- /dev/null
+++ b/source/css/_defines/const.styl
@@ -0,0 +1,50 @@
+
+// device defines
+$device-mobile-s = 320px
+$device-mobile-m = 375px
+$device-mobile-l = 425px
+$device-mobile = 500px
+$device-tablet = 768px
+$device-laptop = 1024px
+$device-desktop = 1440px
+$device-2k = 2048px
+$device-4k = 2560px
+
+// -------- 这些是方便开发预定义的颜色,不需要动态改变 --------
+$color-md-red = #f44336
+$color-md-pink = #E91E63
+$color-md-purple = #9c27b0
+$color-md-deep-purple = #673ab7
+$color-md-indigo = #3f51b5
+$color-md-light-blue = #4BA7EE
+$color-md-blue = #2196f3
+$color-md-deep-blue = #3367d6
+$color-md-teal = #009688
+$color-md-green = #4caf50
+$color-md-light-green = #8bc34a
+$color-md-orange = #ff9800
+$color-md-deep-orange = #ff5722
+$color-md-brown = #795548
+$color-md-blue-grey = #607d8b
+$color-md-grey = #9e9e9e
+$color-md-light-grey =#e0e0e0
+$color-md-yellow = #FCEC60
+$color-md-amber = #F6C344
+
+$color-mac-cyan = #1BCDFC
+$color-mac-green = #3DC550
+$color-mac-yellow = #FFBD2B
+$color-mac-red = #FE5F58
+
+$color-google-blue = #4688F1
+$color-google-green = #3AA757
+$color-google-yellow = #FABB2D
+$color-google-red = #E8453C
+
+$c-red = #F44336
+$c-orange = #FA6400
+$c-yellow = #FFBD2B
+$c-green = #3DC550
+$c-cyan = #1BCDFC
+$c-blue = #2196f3
+$c-purple = #9c27b0
diff --git a/source/css/_defines/func.styl b/source/css/_defines/func.styl
new file mode 100644
index 0000000..864387d
--- /dev/null
+++ b/source/css/_defines/func.styl
@@ -0,0 +1,51 @@
+
+
+scrollbar($w = 4px, $b = 2px, $c = var(--text-meta), $h = var(--text-p3))
+ &::-webkit-scrollbar
+ height: $w
+ width: $w
+ &::-webkit-scrollbar-track-piece
+ background: transparent
+ &::-webkit-scrollbar-thumb
+ background: $c
+ cursor: pointer
+ border-radius: $b
+ &:hover
+ background: $h
+
+
+scrollbar-codeblock()
+ &::-webkit-scrollbar
+ height: 4px
+ width: 4px
+ &::-webkit-scrollbar-track-piece
+ background: transparent
+ &::-webkit-scrollbar-thumb
+ background: transparent
+ cursor: pointer
+ border-radius: $border-block
+ &:hover
+ &::-webkit-scrollbar-thumb
+ background: var(--text-meta)
+ &:hover
+ background: var(--text-p3)
+
+
+hover-block($v, $h, $br = 4px)
+ border-radius: $br
+ padding: $v $h
+ trans2 color background
+ &:hover
+ background: var(--hover-block)
+
+
+inside-box($fs = $fs15)
+ p,ol,ul
+ margin-top: 0.5rem
+ margin-bottom: 0.5rem
+ &:first-child
+ margin-top: 0
+ &:last-child
+ margin-bottom: 0
+ p,li
+ font-size: $fs
diff --git a/source/css/_defines/theme.styl b/source/css/_defines/theme.styl
new file mode 100644
index 0000000..7fceb41
--- /dev/null
+++ b/source/css/_defines/theme.styl
@@ -0,0 +1,27 @@
+:root
+ --site-bg: $c-site-bg-light
+ --block: $c-block-light
+ --block-border: mix($c-block-light, $c-text-light, 95)
+ --hover-block: mix($c-block-light, $c-text-light, 98)
+ --text-p0: $c-title-light
+ --text-p1: $c-text-light
+ --text-p2: mix($c-text-light, $c-site-bg-light, 80)
+ --text-p3: mix($c-text-light, $c-site-bg-light, 60)
+ --text-meta: mix($c-text-light, $c-site-bg-light, 20)
+ --card: $c-card-light
+ --theme-highlight: darken($color-theme, 5)
+ --theme-bg: mix($color-theme, $c-card-light, 10)
+
+body[theme='dark']
+ --site-bg: $c-site-bg-dark
+ --block: $c-block-dark
+ --block-border: mix($c-block-dark, $c-text-dark, 90)
+ --hover-block: mix($c-block-dark, $c-text-dark, 95)
+ --text-p0: $c-title-dark
+ --text-p1: $c-text-dark
+ --text-p2: mix($c-text-dark, $c-site-bg-dark, 70)
+ --text-p3: mix($c-text-dark, $c-site-bg-dark, 45)
+ --text-meta: mix($c-text-dark, $c-site-bg-dark, 30)
+ --card: $c-card-dark
+ --theme-highlight: $color-theme
+ --theme-bg: mix($color-theme, $c-card-dark, 10)
diff --git a/source/css/_layout/base.styl b/source/css/_layout/base.styl
new file mode 100644
index 0000000..1351a7f
--- /dev/null
+++ b/source/css/_layout/base.styl
@@ -0,0 +1,129 @@
+*
+ outline: none
+html
+ font-family: $ff-body
+ font-size: $fs-root
+
+body
+ background: var(--site-bg)
+ margin: 0
+ -webkit-font-smoothing: antialiased
+ -moz-osx-font-smoothing: grayscale
+ text-rendering: optimizelegibility
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
+
+
+h1
+ font-size: $fs-h1
+ font-weight: 600
+
+pre
+ font-family: $ff-code
+ font-size: $fs-code
+ tab-size: 4
+ -moz-tab-size: 4
+ -o-tab-size: 4
+ -webkit-tab-size: 4
+
+a
+ text-decoration: none
+ color: $color-link
+ &:hover
+ color: $color-hover
+
+a.button
+ font-weight: 500
+ line-height: 1
+ padding: 0.75rem 2rem
+ border-radius: 3px
+ trans1: background
+ font-size: $fs15
+ &.cyan
+ background: $color-mac-cyan
+ color: white
+ &:hover
+ background: $color-hover
+
+
+.cap
+ font-weight: 600
+ font-size: $fs12
+ color: var(--text-p3)
+ &.breadcrumb
+ color: $color-cat
+ &.blue
+ color: darken($color-md-blue, 6)
+ &.cyan
+ color: darken($color-mac-cyan, 6)
+ &.green
+ color: darken($color-mac-green, 4)
+
+
+span.dot,span.sep
+ font-size: 0.9em
+ margin: 0 .25em
+span.dot:before
+ content: '·'
+span.sep:before
+ content: '/'
+
+hr
+ color: var(--text-meta)
+ opacity: .1
+
+button
+ padding: 1em
+ border: none
+ font-weight: 600
+ outline: none
+ disable-select()
+ &:hover
+ color: $color-inner
+ background: $color-theme
+
+img
+ max-width: 100%
+
+
+li
+ font-size: $fs15
+
+svg.icon
+ width: 1em
+ height: 1em
+ vertical-align: middle
+ fill: currentColor
+ overflow: hidden
+
+ul,ol
+ padding-left: 1.5rem
+
+ul ul, ul ol
+ padding-left: 0
+ol ul, ol ol
+ padding-left: 0
+
+.dis-select
+ disable-select()
+
+
+// table
+table:not([class])
+ border-collapse: collapse
+ overflow: auto
+ display: inline-block
+ max-width: 100%
+ vertical-align: text-top
+ th
+ background: var(--hover-block)
+ td,th
+ padding: 8px 16px
+ border: 2px solid var(--hover-block)
+ line-height: 1.5
+ font-size: 90%
+ tr
+ word-break: keep-all
+ background: var(--block)
+ trans()
+ &:hover
+ background: var(--hover-block)
diff --git a/source/css/_layout/footer.styl b/source/css/_layout/footer.styl
new file mode 100644
index 0000000..7a51cf8
--- /dev/null
+++ b/source/css/_layout/footer.styl
@@ -0,0 +1,12 @@
+.page-footer
+ margin: $gap
+ font-size: $fs12
+ color: var(--text-p3)
+ p
+ margin: 4px 0
+ line-height: 1.2
+ a
+ color: var(--text-p3)
+ text-decoration: underline
+ &:hover
+ color: $color-hover
diff --git a/source/css/_layout/highlight.styl b/source/css/_layout/highlight.styl
new file mode 100644
index 0000000..5d9bc7e
--- /dev/null
+++ b/source/css/_layout/highlight.styl
@@ -0,0 +1,238 @@
+article.md .highlight
+ margin: 1rem 0
+ border-radius: $border-block
+ -webkit-font-smoothing: auto
+ -moz-osx-font-smoothing: auto
+ overflow: hidden
+ // width: "calc(100% - 2px)"
+ width: 100%
+ background: var(--block)
+ border: 1px solid var(--block-border)
+ line-height: 1.5
+ font-family: $ff-code
+
+article.md .highlight
+ position: relative
+ overflow: auto
+ display: block
+ figcaption
+ color: var(--text-p2)
+ font-size: $fs-code
+ padding: 4px 0.5rem
+ position: sticky
+ left: 0
+ background: var(--hover-block)
+ border-top-left-radius: "calc(%s - 1px)" % $border-block
+ border-top-right-radius: "calc(%s - 1px)" % $border-block
+ border-bottom: 1px solid var(--block-border)
+ span
+ margin-right: 4px
+ >table
+ overflow: auto
+ display: block
+ td,th
+ padding: 0
+ border: none
+ line-height: 1.5
+ margin: 0
+ background: transparent
+ border: none
+ scrollbar-codeblock()
+ tr
+ background: transparent
+ &:hover
+ background: transparent
+
+
+ .gutter
+ pointer-events: none
+ disable-select()
+ text-align: right
+ padding: 0 1em
+ border-width: 0
+ margin-left: 0
+ position: sticky
+ left: 0
+ z-index: 1
+ background: var(--block)
+ pre .line
+ color: var(--text-p2)
+ .code
+ pre
+ display: block
+ padding: .5em 1rem
+ .gutter+.code pre
+ padding-left: 0.25em
+
+
+table:not([class])
+ border-collapse: collapse
+
+article.md .highlight
+ .code
+ vertical-align: top
+ &:before
+ content: ""
+ position: absolute
+ top: 0
+ right: 0
+ padding: 4px 0.5rem
+ opacity: .25
+ font-weight: 700
+
+ &.yaml .code:before
+ content: "YAML"
+ &.json .code:before
+ content: "JSON"
+
+ &.diff .code:before
+ content: "diff"
+
+ &.html .code:before
+ content: "HTML"
+ &.js .code:before,&.javascript .code:before
+ content: "JS"
+ &.css .code:before
+ content: "CSS"
+ &.less .code:before
+ content: "Less"
+ &.stylus .code:before
+ content: "Stylus"
+
+ &.bash .code:before
+ content: "bash"
+ &.shell .code:before
+ content: "shell"
+ &.sh .code:before
+ content: "sh"
+ &.ini .code:before
+ content: "ini"
+
+ &.c .code:before
+ content: "C"
+ &.cpp .code:before
+ content: "C++"
+ &.objc .code:before,&.objectivec .code:before
+ content: "Objective-C"
+ &.swift .code:before
+ content: "Swift"
+
+ &.java .code:before
+ content: "Java"
+ &.python .code:before
+ content: "Python"
+ &.php .code:before
+ content: "PHP"
+ &.rust .code:before
+ content: "Rust"
+ &.sql .code:before
+ content: "SQL"
+ &.ruby .code:before
+ content: "Ruby"
+ &.makefile .code:before
+ content: "Makefile"
+ &.go .code:before
+ content: "Go"
+ &.typescript .code:before
+ content: "TypeScript"
+
+
+
+$hl-keyword = #9c27b0
+$hl-blue = #1E80F0
+$hl-cyan = #17AFCA
+$hl-green = #3FA33F
+$hl-light-blue = #6ECDF9
+$hl-red = #EE2B29
+$hl-orange = #FB3F1B
+$hl-amber = #FD8607
+$hl-text = var(--text-p1)
+pre
+ .code:before
+ display: none
+ // 行
+ .line,.params
+ color: $hl-text
+
+ .line .addition
+ color: $hl-green
+ .line .deletion
+ color: $hl-red
+
+ .marked
+ background-color: alpha(#FED542, .4)
+ padding: 2px 8px 2px 0
+ border-radius: 2px
+ width: 100%
+
+ .title, .attr, .attribute
+ color: $color-md-indigo
+
+ // 注释
+ .comment
+ color: var(--text-p3)
+
+ .keyword, .meta-keyword, .javascript .function
+ color: $hl-keyword
+
+ .type, .built_in, .tag .name
+ color: $color-md-light-blue
+
+ .variable, .regexp, .ruby .constant, .xml .tag .title, .xml .pi, .xml .doctype, .html .doctype, .css .id, .css .class, .css .pseudo
+ color: $hl-amber
+
+ .number, .preprocessor, .literal, .constant
+ color: $hl-amber
+
+ .class, .ruby .class .title, .css .rules .attribute
+ color: $color-md-orange
+
+ .string, .meta-string
+ color: darken($color-md-green, 10%)
+
+ .value, .inheritance, .header, .ruby .symbol, .xml .cdata
+ color: $color-md-green
+
+ .css .hexcolor
+ color: #66cccc
+
+ .function, .python .decorator, .python .title, .ruby .function .title, .ruby .title .keyword, .perl .sub, .javascript .title, .coffeescript .title
+ color: #6699cc
+
+
+.highlight.html,.highlight.css,.highlight.less,.highlight.stylus
+ .line
+ .tag .name, .selector-tag
+ color: $hl-red
+ .selector-class, .selector-attr
+ color: $hl-amber
+ .attribute
+ color: $color-md-indigo
+ .number
+ color: $hl-cyan
+
+
+.highlight.objc,.highlight.objectivec,.highlight.swift,.highlight.c
+ .line
+ .meta
+ color: $hl-keyword
+ .meta-string,.string
+ color: $hl-orange
+ .class
+ color: $hl-text
+ .title
+ color: $hl-blue
+ .comment
+ color: $hl-green
+
+.highlight.json
+ .line
+ .attr
+ color: #E24F5A
+ .literal
+ color: $color-md-indigo
+
+.highlight.yaml
+ .line
+ .attr
+ color: #E24F5A
diff --git a/source/css/_layout/index.styl b/source/css/_layout/index.styl
new file mode 100644
index 0000000..b29a08b
--- /dev/null
+++ b/source/css/_layout/index.styl
@@ -0,0 +1,4 @@
+@import 'partial/*'
+@import 'tag-plugins/*'
+@import 'sidebar/*'
+@import 'pages/*'
diff --git a/source/css/_layout/layout.styl b/source/css/_layout/layout.styl
new file mode 100644
index 0000000..a3a6231
--- /dev/null
+++ b/source/css/_layout/layout.styl
@@ -0,0 +1,79 @@
+.l_body
+ display: flex
+ margin: auto
+ padding: 0 $gap
+ justify-content: center
+
+.l_body .l_left
+ z-index: 8
+ width: $sidebar
+ flex-shrink: 0
+ height 100vh
+ position: sticky
+ position: -webkit-sticky
+ top: 0
+
+.l_body .l_main
+ flex-shrink: 1
+ flex-grow: 1
+ // margin: var(--gap) var(--gap) var(--gap) var(--gap_2)
+ // width: "calc(100% - %s)" % ($sidebar + 18 * $gap)
+ width: 320px
+ max-width: $layout-max-body-width
+ footer
+ margin-top: 4rem
+ margin-bottom: 2rem
+
+.float-panel
+ position: sticky
+ right: 0
+ bottom: 2rem
+ float: right
+ z-index: 9
+ display: flex
+ border-radius: 2rem 0 0 2rem
+ padding-right: 2rem
+ padding: 4px
+ overflow: hidden
+ background: var(--hover-block)
+.sidebar-toggle.mobile
+ cursor: pointer
+ color: var(--text-p2)
+ background: white
+ padding: 0.5rem
+ margin-right: 1rem
+ border-radius: 32px
+ line-height: 1
+ width: 2rem
+ height: 2rem
+ font-size: 13px
+
+.l_body.mobile.sidebar
+ .sidebar-toggle.mobile
+ background: $color-hover
+ color: white
+
+
+// iPad 竖屏
+@media screen and (max-width: $device-tablet)
+ .mobile-only
+ display: block !important
+ .l_body
+ padding: 0
+ .l_left
+ position: fixed
+ height: 100vh
+ width: 280px
+ transform: translateX(0 - $gap - 320px)
+ margin: 0
+ left: 0
+ background: var(--site-bg)
+ box-shadow: $boxshadow-card-float
+ .l_main
+ max-width: 100%
+ .l_body.mobile
+ .l_left
+ transition: transform .38s ease-out
+ .l_body.mobile.sidebar
+ .l_left
+ transform: translateX(0px)
diff --git a/source/css/_layout/main.styl b/source/css/_layout/main.styl
new file mode 100644
index 0000000..e12c02e
--- /dev/null
+++ b/source/css/_layout/main.styl
@@ -0,0 +1,26 @@
+.l_main
+ position: relative
+ header
+ display: none
+ margin: $gap * 2 $gap $gap
+ padding: 0.25rem
+ background: var(--block)
+ border-radius: 8px
+ .logo-wrap
+ margin: 0.5rem 1rem
+ .img
+ width: 32px
+ height: 32px
+ display: none
+ .title
+ font-size: 1.5rem
+
+// pc上右边顶部留白
+@media screen and (min-width: $device-tablet)
+ .l_main
+ margin-top: $gap * 2
+
+// 移动端显示主导航栏
+@media screen and (max-width: $device-tablet)
+ .l_main.list header
+ display: block
diff --git a/source/css/_layout/md.styl b/source/css/_layout/md.styl
new file mode 100644
index 0000000..df5e6ba
--- /dev/null
+++ b/source/css/_layout/md.styl
@@ -0,0 +1,83 @@
+article.md
+ max-width: 100%
+ padding: 1rem
+ overflow: hidden
+ color: var(--text-p1)
+ line-height: 1.7
+
+article.md.content
+ margin-bottom: $gap * 2
+
+// titles
+article.md
+ h1.article-title
+ margin-top: $gap * 0.5
+ line-height: 1.2
+ h1,h2,h3,h4,h5,h6
+ line-height: 1.8
+ color: var(--text-p0)
+ &:hover
+ a.headerlink:before
+ opacity: 1
+ a.headerlink:before
+ opacity: 0
+ content: '#'
+ position: absolute
+ width: 0.75em
+ margin-left: -0.75em
+ h2
+ margin-top: 3rem
+ border-bottom: 1px solid var(--block-border)
+ font-weight: 400
+ h3
+ margin-top: 3rem
+ margin-bottom: 0
+ font-weight: 500
+ h2+h3
+ margin-top: 1rem
+ h3+h4
+ margin-top: 1rem
+ h5
+ font-size: $fs14
+ h6
+ font-size: $fs12
+
+// a
+article.md
+ p>a
+ trans1: color
+ &:not([class])
+ font-weight: 500
+ &:hover
+ text-decoration: underline
+
+// code
+article.md code
+ color: $color-inlinecode
+ font-family: $ff-code
+ font-size: $fs14
+ word-break: break-all
+
+
+// div
+article.md>div
+ margin: 1rem 0
+// blockquote
+article.md blockquote
+ margin: 1rem 0
+ padding: 1rem
+ background: var(--block)
+ border-left: $border-block solid $color-theme
+ border-radius: 0 $border-block $border-block 0
+ color: var(--text-p2)
+ p
+ margin: 0.5em 0
+ font-size: $fs14
+
+article.md ul, article.md ol
+ padding-left: 2rem
+
+article.md img
+ border-radius: 4px
+ margin: auto
+ display: block
diff --git a/source/css/_layout/navbar.styl b/source/css/_layout/navbar.styl
new file mode 100644
index 0000000..0c1e619
--- /dev/null
+++ b/source/css/_layout/navbar.styl
@@ -0,0 +1,51 @@
+nav.cap
+ position: sticky
+ position: -webkit-sticky
+ margin-top: 1.75rem
+ top: -2px
+ display: flex
+ background: var(--site-bg)
+ padding: 0.5rem 1.25rem 0.38rem
+ z-index: 1
+ overflow: scroll
+ scrollbar(0, 0)
+ >p
+ margin: 0
+ a
+ padding: 0.5rem 0.75rem
+ color: var(--text-p3)
+ border-radius: 4px
+ font-weight: 600
+ white-space: nowrap
+ trans1 background
+ &:hover
+ background: var(--hover-block)
+ color: var(--text-p1)
+ &.active
+ font-weight: 700
+ color: inherit
+
+.md .tag-plugin.navbar
+ nav.cap
+ margin: 0
+ padding: 1px
+ background: var(--block)
+ border-radius: 6px
+ a
+ margin: 1px
+ padding: 0.5em 0.75rem
+ &:hover
+ background: #fff
+ color: var(--text-p1)
+
+// 面包屑导航
+.breadcrumb-navigation
+ padding: $gap $gap 0
+ margin-top: $gap
+ font-size: $fs-footnote
+ color: var(--text-p3)
+ font-weight: 500
+ a.cap:hover
+ color: $color-cat-hover
+ div+div
+ margin-top: 2px
diff --git a/source/css/_layout/pages/archives.styl b/source/css/_layout/pages/archives.styl
new file mode 100644
index 0000000..3cac768
--- /dev/null
+++ b/source/css/_layout/pages/archives.styl
@@ -0,0 +1,15 @@
+.post-list .post-card#archive
+ a.post
+ display: inline-flex
+ align-items: baseline
+ font-size: $fs14
+ margin: 0.25rem 0
+ font-weight: 600
+ color: var(--text-p2)
+ &:hover
+ color: $color-hover
+ time
+ font-family: $ff-code
+ margin-right: 1em
+ flex-shrink: 0;
+ opacity 0.5
diff --git a/source/css/_layout/pages/categories.styl b/source/css/_layout/pages/categories.styl
new file mode 100644
index 0000000..c15de63
--- /dev/null
+++ b/source/css/_layout/pages/categories.styl
@@ -0,0 +1,16 @@
+.post-list .post-card#cats
+ a.cat
+ display: flex
+ font-size: $fs14
+ &.child
+ padding-left: 2rem
+ font-weight: 600
+ padding: 0.5rem 1rem
+ border-radius: $border-block
+ color: var(--text-p2)
+ &:hover
+ background: var(--block)
+ color: $color-hover
+ .badge
+ opacity 0.5
+ margin-left: 4px
diff --git a/source/css/_layout/pages/error.styl b/source/css/_layout/pages/error.styl
new file mode 100644
index 0000000..fd48438
--- /dev/null
+++ b/source/css/_layout/pages/error.styl
@@ -0,0 +1,21 @@
+article.md.error-page
+ text-align: center
+ margin-top: 2rem
+ img#error
+ width: 30vw
+ max-height: 150px
+ margin-bottom: 2rem
+ h1
+ font-size: 4rem
+ margin-bottom: 0
+ p.what,p.why
+ margin: 0.5em
+ p.why
+ font-size: $fs13
+ a#back
+ margin: 2rem 0
+ display: inline-block
+
+@media screen and (max-width: $device-tablet)
+ article.md.error-page
+ margin-top: 4rem
diff --git a/source/css/_layout/pages/friends.styl b/source/css/_layout/pages/friends.styl
new file mode 100644
index 0000000..069dcfa
--- /dev/null
+++ b/source/css/_layout/pages/friends.styl
@@ -0,0 +1,61 @@
+.friends-wrap
+ .group-header
+ margin: 1rem 0
+ p
+ margin: 0
+ p.title
+ font-size: 1.125rem
+ font-weight: 500
+ p.description
+ font-size: $fs14
+ .group-body
+ display: grid
+ grid-gap: 4px 4px
+ grid-template-columns: repeat(auto-fill, 96px)
+ margin-bottom: 2rem
+
+.friends-wrap .user-simple
+ a
+ margin: auto
+ color: var(--text-p1)
+ font-size: $fs12
+ display: flex
+ flex-direction: column
+ align-items: center
+ text-align: center
+ line-height: 1.2
+ border-radius: 4px
+ overflow: hidden
+ position: relative
+ background: var(--card)
+ img
+ object-fit: cover
+ display: block
+ width: 64px
+ height: 64px
+ .name
+ padding-top: 0.5em
+
+// style
+.friends-wrap.round .user-simple
+ a
+ padding: 0.5rem
+ trans1: background
+ img
+ border-radius: 64px
+ &:hover
+ background: var(--hover-block)
+
+.friends-wrap.rect .user-simple
+ a
+ padding: 0
+ display: block
+ trans1: box-shadow
+ img
+ width: 96px
+ height: 96px
+ border-radius: 0
+ .name
+ padding: 0.5em
+ &:hover
+ box-shadow: $boxshadow-card-float
diff --git a/source/css/_layout/pages/tags.styl b/source/css/_layout/pages/tags.styl
new file mode 100644
index 0000000..cee53fe
--- /dev/null
+++ b/source/css/_layout/pages/tags.styl
@@ -0,0 +1,17 @@
+.post-list .post-card#tags
+ a.tag
+ display: inline-flex
+ align-items: center
+ margin: 0.25rem 0.5rem
+ position: relative
+ font-weight: 500
+ color: var(--text-p2)
+ &:hover
+ color: $color-hover
+ span
+ margin: 0 0.25rem
+ .name
+ font-size: $fs14
+ .badge
+ opacity 0.5
+ font-size: $fs12
diff --git a/source/css/_layout/partial/comments.styl b/source/css/_layout/partial/comments.styl
new file mode 100644
index 0000000..02cb7e2
--- /dev/null
+++ b/source/css/_layout/partial/comments.styl
@@ -0,0 +1,8 @@
+.comments-wrap
+ padding: $gap * 2 $gap
+ div.cmt-body.utterances
+ margin: 0 -4px
+
+ a.avatar
+ border-radius: 120px !important
+ overflow: hidden !important
diff --git a/source/css/_layout/partial/cover.styl b/source/css/_layout/partial/cover.styl
new file mode 100644
index 0000000..27f7a65
--- /dev/null
+++ b/source/css/_layout/partial/cover.styl
@@ -0,0 +1,30 @@
+.l_cover
+ height: 100vh
+ text-align: center
+ display: flex
+ flex-direction: column
+ justify-content: center
+ align-items: center
+
+.l_cover .cover-wrap
+ margin-bottom: 0
+ max-width: 500px
+ .logo
+ margin-bottom: 2rem
+ img
+ object-fit: contain
+ max-height: 35vh
+ max-width: 100%
+ .cover-title
+ font-weight: 700
+ font-size: 1.5rem
+ margin: 1rem 0
+ &:first-child
+ font-size: 3rem
+ .description
+ margin: 1rem 0
+ .start-wrap
+ margin: $gap * 2 0
+ flex-shrink: 0
+ a.start
+ display: inline-block
diff --git a/source/css/_layout/partial/lazyload.styl b/source/css/_layout/partial/lazyload.styl
new file mode 100644
index 0000000..8c284f5
--- /dev/null
+++ b/source/css/_layout/partial/lazyload.styl
@@ -0,0 +1,14 @@
+img
+ max-width 100%
+ &.lazyload:not(.placeholder)
+ transition opacity .5s ease-out 0s
+ &:not(.loaded)
+ opacity: 0
+ &.loaded
+ opacity: 1
+ if hexo-config('plugins.lazyload.blurIn') == true
+ transition filter .25s ease-out 0s
+ &:not(.loaded)
+ filter blur(8px)
+ &.loaded
+ filter none
diff --git a/source/css/_layout/partial/paginator.styl b/source/css/_layout/partial/paginator.styl
new file mode 100644
index 0000000..8bcbffd
--- /dev/null
+++ b/source/css/_layout/partial/paginator.styl
@@ -0,0 +1,22 @@
+.posts .paginator-wrap
+ display: flex
+ justify-content: space-between
+ align-items: center
+ disable-select()
+ font-weight: 500
+ svg.icon
+ font-size: 1.8rem
+ .paginator
+ text-align: center
+ padding: $gap
+ div.paginator
+ font-size: $fs14
+ color: var(--text-p2)
+ a.paginator
+ color: $color-link
+ trans1: color
+ &.disable
+ pointer-events: none
+ opacity: 0.25
+ a.paginator:hover
+ color: $color-hover
diff --git a/source/css/_layout/partial/read_next.styl b/source/css/_layout/partial/read_next.styl
new file mode 100644
index 0000000..e49458b
--- /dev/null
+++ b/source/css/_layout/partial/read_next.styl
@@ -0,0 +1,18 @@
+.read-next-wrap
+ padding: $gap * 2 $gap
+ .body
+ .post-title
+ margin: 0.5rem 0
+ line-height: 1.2
+ font-size: $fs14
+ color: var(--text-p2)
+ font-weight: 500
+ a
+ color: inherit
+ &:hover
+ color: $color-hover
+ &.read
+ color: var(--text-p3)
+ font-size: $fs12
+ &.unread
+ font-size: 1rem
diff --git a/source/css/_layout/partial/references.styl b/source/css/_layout/partial/references.styl
new file mode 100644
index 0000000..aa7c9ce
--- /dev/null
+++ b/source/css/_layout/partial/references.styl
@@ -0,0 +1,16 @@
+.references-wrap
+ margin: $gap * 2 $gap
+ padding: 1rem
+ background: var(--block)
+ border-radius: $border-block
+ .header
+ font-weight: 500
+ color: var(--text-p3)
+ margin-bottom: 1rem
+ .body
+ ul
+ margin: 0
+ .post-title
+ margin: 0.5rem 0
+ line-height: 1.2
+ font-size: $fs13
diff --git a/source/css/_layout/partial/related_posts.styl b/source/css/_layout/partial/related_posts.styl
new file mode 100644
index 0000000..4077ac7
--- /dev/null
+++ b/source/css/_layout/partial/related_posts.styl
@@ -0,0 +1,71 @@
+div.related-posts-wrap
+ padding: $gap * 2 $gap
+ &:empty
+ display: none
+ .related-posts
+ width: 100%
+ display: flex
+ margin: $gap 0
+ overflow-x: scroll
+ disable-select()
+ align-items: flex-start
+ scrollbar(8px, 4px)
+ padding-bottom: 4px
+ a
+ flex-grow: 0
+ flex-shrink: 0
+ background: var(--card)
+ border-radius: 0.5rem
+ padding-bottom: 1rem
+ line-height: 1.2
+ display: flex
+ flex-direction: column
+ overflow: hidden
+ width: 360px
+ opacity: 0.7
+ trans1: opacity 0.5s
+ border: 1px solid var(--hover-block)
+ .img
+ height: 180px
+ overflow: hidden
+ img
+ width: 100%
+ height: 100%
+ object-fit: cover
+ @media screen and (max-width: $device-tablet)
+ width: 300px
+ .img
+ height: 150px
+ @media screen and (max-width: $device-mobile)
+ width: 300px
+ .title
+ color: var(--text-p1)
+ font-weight: 500
+ font-size: $fs15
+ margin: 1rem 1rem 0 1rem
+ display: -webkit-box
+ -webkit-box-orient: vertical
+ overflow: hidden
+ -webkit-line-clamp: 2
+ .excerpt
+ color: var(--text-p2)
+ font-size: $fs13
+ margin: 0.4rem 1rem 0 1rem
+ display: -webkit-box
+ -webkit-box-orient: vertical
+ overflow: hidden
+ -webkit-line-clamp: 3
+ &:hover
+ opacity: 1
+ a+a
+ margin-left: $gap
+
+
+// // iPad 竖屏
+// @media screen and (max-width: $device-tablet)
+// section.related-posts-wrap
+// .related-posts
+// a
+// min-width: 280px
+// .img
+// height: 120px
diff --git a/source/css/_layout/post_list.styl b/source/css/_layout/post_list.styl
new file mode 100644
index 0000000..39495c1
--- /dev/null
+++ b/source/css/_layout/post_list.styl
@@ -0,0 +1,107 @@
+// list
+.post-list
+ margin: 1rem
+ .card-title,.list-title
+ font-weight: 600
+ color: var(--text-meta)
+ font-family: $ff-code
+ line-height: 1.2
+ .list-title
+ margin: 2rem 1rem 0rem
+ font-size: 1.5rem
+ .card-title
+ padding-bottom: 0.5rem
+ font-size: 1.2rem
+
+// card
+.post-list .post-card
+ display: block
+ margin: 1rem 0
+ color: var(--text-p2)
+ border-radius: $border-card
+ box-shadow: 0 2px 8px 0px rgba(0, 0, 0, 0.02)
+ trans1 box-shadow
+ overflow: hidden
+ background: var(--card)
+ .meta.cap
+ margin-bottom: 0.5rem
+ span+span
+ margin-left: 0.5rem
+.post-list .post-card:hover
+ box-shadow: 0 2px 8px 0px rgba(0, 0, 0, 0.04), 0 4px 16px 0px rgba(0, 0, 0, 0.04)
+ .logo img
+ transform: scale(1.1)
+ .excerpt .readmore
+ color: $color-link
+
+// common article
+.post-list article
+ padding: 1rem
+
+// posts
+.post-list .post-card
+ .post-title
+ font-weight: 500
+ margin: .5em 0
+ line-height: 1.2
+ font-size: $fs-h3
+ border-bottom: none
+ color: var(--text-p1)
+ trans1 color
+ .excerpt
+ font-size: $fs14
+ .post-cover-wrap
+ overflow: hidden
+ margin-left: 0 - 1.5 * $gap
+ margin-top: 0 - 1.5 * $gap
+ margin-bottom: 1.25 * $gap
+ width: "calc(100% + 3 * %s)" % $gap
+ .post-cover
+ background-position: center
+ background-size: cover
+ height: 320px
+ @media screen and (max-width: 900px)
+ height: 280px
+ @media screen and (max-width: $device-tablet)
+ height: 320px
+ @media screen and (max-width: $device-mobile)
+ height: 280px
+ @media screen and (max-width: $device-mobile-l)
+ height: 240px
+ @media screen and (max-width: $device-mobile-m)
+ height: 200px
+ trans1: transform 1s
+.post-list .post-card.post:hover
+ .post-title
+ color: $color-hover
+ .post-cover
+ transform: scale(1.04)
+
+// wiki
+.post-list .post-card.wiki article
+ display: flex
+ flex-wrap: wrap
+ trans1 box-shadow
+ justify-content: center
+
+
+.post-list .post-card.wiki article .logo
+ display: flex
+ width: 180px
+ margin: 1rem
+ align-items: center
+ img
+ object-fit: contain
+ trans1 transform 0.75s
+.post-list .post-card.wiki article .excerpt
+ margin: 1rem
+ min-width: 220px
+ flex: 1
+ overflow: hidden
+ word-wrap: break-word
+ .title
+ font-weight: 600
+ font-size: $fs-h3
+ >p
+ margin: 0.5rem 0
+ font-size: $fs14
diff --git a/source/css/_layout/sidebar.styl b/source/css/_layout/sidebar.styl
new file mode 100644
index 0000000..adb2c9f
--- /dev/null
+++ b/source/css/_layout/sidebar.styl
@@ -0,0 +1,133 @@
+.l_left .wrap
+ display: flex
+ flex-direction: column
+ height: "calc(100% - 4 * %s)" % $gap
+ margin-top: $gap * 2
+ padding: $gap
+ @media screen and (max-width: $device-tablet)
+ height: "calc(100% - 2 * %s)" % $gap
+ margin-top: 0
+
+.logo-wrap
+ margin: $gap 0
+ display: flex
+ align-items: center
+ color: var(--text-p0)
+ &:hover
+ color: var(--text-p0)
+ .img
+ width: 40px
+ height: 40px
+ flex-shrink: 0
+ border-radius: 20px
+ overflow: hidden
+ margin-right: 1rem
+ img
+ object-fit: cover
+ .title
+ padding-top: 2px
+ font-size: 1.75rem
+ font-weight: 900
+ color: inherit
+ font-family: $ff-logo
+
+nav.menu
+ margin-top: 0
+ background: var(--block)
+ border-radius: 6px
+ disable-select()
+ display: flex
+ padding: 1px
+ flex-wrap: wrap
+ &::-webkit-scrollbar
+ display: none
+ &::-webkit-scrollbar-track-piece
+ background: transparent
+ &::-webkit-scrollbar-thumb
+ display: none
+ a.nav-item
+ text-overflow: ellipsis
+ word-break: keep-all
+ margin: 1px
+ border-radius: 4px
+ font-size: $fs14
+ font-weight: 500
+ trans2: color background
+ overflow: hidden
+ span
+ color: var(--text-p3)
+ display: block
+ padding: 6px 12px
+ &.active span, &:hover span
+ color: var(--text-p1)
+ background: var(--card)
+
+.l_left .widgets
+ margin: $gap 0
+ overflow: scroll
+ flex-grow: 1
+ scrollbar(0, 0)
+ .widget-wrap
+ margin: 1rem 0 2rem 0
+ .widget-header
+ display: flex
+ font-size: 1rem
+ color: var(--text-meta)
+ justify-content: space-between
+ align-items: center
+ font-weight: 600
+ position: sticky
+ position: -webkit-sticky
+ top: -2px
+ background: var(--site-bg)
+ padding: 2px 0
+ z-index 1
+ .name
+ font-size: 1rem
+ .cap-action
+ hover-block 4px 4px
+ line-height: 0
+ color: var(--text-meta)
+ trans2: color background
+ .icon
+ fill: var(--text-meta)
+ &:hover
+ color: $color-hover
+ .icon
+ fill: $color-hover
+
+ .widget-body
+ margin: 0.5rem 0
+ font-size: $fs14
+ color: var(--text-p2)
+ p
+ margin-top: 0.5em
+ margin-bottom: 0.5em
+ >a
+ &:not([class])
+ font-weight: 500
+ &:hover
+ text-decoration: underline
+
+
+
+.l_left .footer
+ margin-top: 0
+ margin-bottom: 1rem
+ line-height: 0
+ .social-wrap
+ display: grid
+ grid-gap: $gap * 0.5 $gap * 0.5
+ grid-template-columns: repeat(auto-fill, 32px)
+ a
+ display: inline-block
+ background: var(--card)
+ padding: 4px
+ border-radius: 4px
+ filter: grayscale(100%)
+ overflow: hidden
+ trans1: box-shadow
+ &:hover
+ color: $color-hover
+ filter: grayscale(0%)
+ box-shadow: $boxshadow-float
diff --git a/source/css/_layout/sidebar/recent.styl b/source/css/_layout/sidebar/recent.styl
new file mode 100644
index 0000000..408d81a
--- /dev/null
+++ b/source/css/_layout/sidebar/recent.styl
@@ -0,0 +1,10 @@
+.widgets
+ .post-title
+ margin: 0.5rem 0
+ line-height: 1.2
+ font-size: $fs14
+ font-weight: 500
+ a
+ color: inherit
+ &:hover
+ color: $color-hover
diff --git a/source/css/_layout/sidebar/toc.styl b/source/css/_layout/sidebar/toc.styl
new file mode 100644
index 0000000..9eb0a91
--- /dev/null
+++ b/source/css/_layout/sidebar/toc.styl
@@ -0,0 +1,74 @@
+// toc padding
+#toc .widget-body
+ line-height: 1.2
+ .toc
+ padding: 0
+ margin: 0
+ padding-left: 0.25rem
+ .toc-item .toc-link
+ padding: 0.5rem 0 0.5rem 1rem
+ font-weight: 500
+ font-size: $fs13
+ color: var(--text-p2)
+ .toc-child .toc-item .toc-link
+ padding: 0.25rem 0 0.25rem 1.8rem
+ font-weight: 400
+ color: var(--text-p2)
+ .toc-child .toc-child .toc-item .toc-link
+ padding-left: 2.6rem
+ font-size: $fs12
+ color: var(--text-p3)
+ .toc-child .toc-child .toc-child .toc-item .toc-link
+ padding-left: 3.4rem
+ .toc:only-child
+ .toc-item .toc-link
+ padding-left: 0.5rem
+ .toc-child .toc-item .toc-link
+ padding-left: 1.3rem
+ .toc-child .toc-child .toc-item .toc-link
+ padding-left: 2.1rem
+ .toc-child .toc-child .toc-child .toc-item .toc-link
+ padding-left: 2.9rem
+
+
+#toc .toc-item
+ color: var(--text-p2)
+ font-size: $fs12
+ padding: 0
+ list-style: none
+ &.active
+ color: $color-theme
+ border-left-color: @color
+ .toc-child .toc-item
+ padding: 0
+#toc a.toc-link
+ color: inherit
+ display: block
+ line-height: 1.2
+ border-radius: 4px
+ &:hover
+ background: var(--hover-block)
+ &.active
+ color: var(--theme-highlight) !important
+
+#toc .doc-tree
+ border-left: 2px solid var(--hover-block)
+ a.doc-tree-link
+ color: var(--text-p3)
+ padding: 0.5rem
+ display: block
+ border-top-right-radius: 4px
+ border-bottom-right-radius: 4px
+ font-size: $fs12
+ font-weight: 500
+ &.active
+ color: var(--text-p2)
+ font-size: $fs14
+ &:hover
+ background: var(--hover-block)
+
+#toc .widget-body.wiki
+ .doc-tree.active
+ border-color: var(--text-meta)
+ .doc-tree.active:only-child
+ border-color: var(--hover-block)
diff --git a/source/css/_layout/tag-plugins/about.styl b/source/css/_layout/tag-plugins/about.styl
new file mode 100644
index 0000000..937a12d
--- /dev/null
+++ b/source/css/_layout/tag-plugins/about.styl
@@ -0,0 +1,58 @@
+.tag-plugin.about
+ background: var(--block)
+ border-radius: 6px
+ padding: 2rem
+ .about-header
+ display flex
+ flex-direction: row-reverse
+ justify-content: space-between
+ flex-wrap: wrap
+ img
+ object-fit: contain
+ >img
+ margin: auto 0
+ >p
+ font-size: $fs12
+ font-weight: 500
+ color: var(--text-p3)
+ padding-top: 0.75rem
+ strong:first-child
+ font-size: 3rem
+ font-weight: 700
+ color: var(--text-p1)
+ margin-right: 0.75rem
+ .avatar
+ display: inline-flex
+ margin: 0 1rem
+ // flex-shrink: 0
+ .about-body
+ >p
+ font-size: $fs14
+ line-height: 1.5
+ .tag-plugin.navbar:last-child
+ // margin: 0 -0.75rem -0.75rem
+ nav.cap
+ padding: 0
+ background: none
+ a
+ margin: 0
+ padding: 0
+ color: var(--text-p1)
+ color: $color-link
+ &:hover
+ color: $color-hover
+ background: none
+ a+a
+ margin-left: 1rem
+ .about-header+.about-body
+ margin-top: 1.5rem
+
+@media screen and (max-width: $device-mobile)
+ .tag-plugin.about
+ background: none
+ padding: 1rem
+ .about-header
+ .avatar
+ margin: 1rem auto
+ p
+ width 100%
diff --git a/source/css/_layout/tag-plugins/checkbox.styl b/source/css/_layout/tag-plugins/checkbox.styl
new file mode 100644
index 0000000..ad1ad53
--- /dev/null
+++ b/source/css/_layout/tag-plugins/checkbox.styl
@@ -0,0 +1,178 @@
+.md .checkbox
+ display: flex
+ align-items: center
+ margin: 0
+ font-size: $fs15
+ input
+ -webkit-appearance: none
+ -moz-appearance: none
+ -ms-appearance: none
+ -o-appearance: none
+ appearance: none
+ position: relative
+ height: 16px
+ width: 16px
+ cursor: pointer
+ display: inline-block
+ outline: none
+ border-radius: 2px
+ flex-shrink: 0
+ margin-right: 8px
+ if hexo-config('tag_plugins.checkbox.interactive') != true
+ pointer-events: none
+ /* Checkbox */
+ input[type=checkbox]
+ &:before, &:after
+ position: absolute
+ content: ""
+ background: var(--site-bg)
+ &:before
+ left: 1px
+ top: 5px
+ width: 0px
+ height: 2px
+ transform: rotate(45deg)
+ -webkit-transform: rotate(45deg)
+ -moz-transform: rotate(45deg)
+ -ms-transform: rotate(45deg)
+ -o-transform: rotate(45deg)
+ &:after
+ right: 7px
+ bottom: 3px
+ width: 2px
+ height: 0px
+ transform: rotate(40deg)
+ -webkit-transform: rotate(40deg)
+ -moz-transform: rotate(40deg)
+ -ms-transform: rotate(40deg)
+ -o-transform: rotate(40deg)
+ &:checked
+ &:before
+ left: 0px
+ top: 7px
+ width: 6px
+ height: 2px
+ &:after
+ right: 3px
+ bottom: 1px
+ width: 2px
+ height: 10px
+ &[symbol=minus] input[type=checkbox]
+ &:before
+ transform: rotate(0)
+ left: 1px
+ top: 5px
+ width: 0px
+ height: 2px
+ &:after
+ transform: rotate(0)
+ left: 1px
+ top: 5px
+ width: 0px
+ height: 2px
+ &:checked
+ &:before
+ left: 1px
+ top: 5px
+ width: 10px
+ height: 2px
+ &:after
+ left: 1px
+ top: 5px
+ width: 10px
+ height: 2px
+ &[symbol=plus] input[type=checkbox]
+ &:before
+ transform: rotate(0)
+ left: 1px
+ top: 5px
+ width: 0px
+ height: 2px
+ &:after
+ transform: rotate(0)
+ left: 5px
+ top: 1px
+ width: 2px
+ height: 0px
+ &:checked
+ &:before
+ left: 1px
+ top: 5px
+ width: 10px
+ height: 2px
+ &:after
+ left: 5px
+ top: 1px
+ width: 2px
+ height: 10px
+ &[symbol=times] input[type=checkbox]
+ &:before
+ transform: rotate(45deg)
+ left: 3px
+ top: 1px
+ width: 0px
+ height: 2px
+ &:after
+ transform: rotate(135deg)
+ right: 3px
+ top: 1px
+ width: 0px
+ height: 2px
+ &:checked
+ &:before
+ left: 1px
+ top: 5px
+ width: 10px
+ height: 2px
+ &:after
+ right: 1px
+ top: 5px
+ width: 10px
+ height: 2px
+ /* Radio */
+ input[type=radio]
+ border-radius: 50%
+ transform: translateY(-1px)
+ &:before
+ content: ""
+ display: block
+ width: 8px
+ height: 8px
+ border-radius: 50%
+ margin: 2px
+ transform: scale(0)
+ &:checked:before
+ transform: scale(1)
+ /* Colors */
+ input
+ border: 2px solid var(--theme)
+ &[type=checkbox]:checked
+ background: var(--theme)
+ &[type=radio]:checked:before
+ background: var(--theme)
+
+
+
+.tag-plugin.checkbox
+ --theme: $color-theme
+
+.tag-plugin.checkbox[color='red']
+ --theme: mix($c-red, $c-card-light, 90)
+
+.tag-plugin.checkbox[color='orange']
+ --theme: mix($c-orange, $c-card-light, 90)
+
+.tag-plugin.checkbox[color='yellow']
+ --theme: mix($c-yellow, $c-card-light, 90)
+
+.tag-plugin.checkbox[color='green']
+ --theme: mix($c-green, $c-card-light, 90)
+
+.tag-plugin.checkbox[color='cyan']
+ --theme: mix($c-cyan, $c-card-light, 90)
+
+.tag-plugin.checkbox[color='blue']
+ --theme: mix($c-blue, $c-card-light, 100)
+
+.tag-plugin.checkbox[color='purple']
+ --theme: mix($c-purple, $c-card-light, 90)
diff --git a/source/css/_layout/tag-plugins/common.styl b/source/css/_layout/tag-plugins/common.styl
new file mode 100644
index 0000000..b15986d
--- /dev/null
+++ b/source/css/_layout/tag-plugins/common.styl
@@ -0,0 +1,78 @@
+.tag-plugin
+ --theme: var(--block-border)
+ --theme-bg: var(--block)
+
+.tag-plugin[color='red']
+ --theme: mix($c-red, $c-card-light, 40)
+ --theme-bg: mix($c-red, $c-card-light, 10)
+
+.tag-plugin[color='orange']
+ --theme: mix($c-orange, $c-card-light, 40)
+ --theme-bg: mix($c-orange, $c-card-light, 10)
+
+.tag-plugin[color='yellow']
+ --theme: mix($c-yellow, $c-card-light, 40)
+ --theme-bg: mix($c-yellow, $c-card-light, 10)
+
+.tag-plugin[color='green']
+ --theme: mix($c-green, $c-card-light, 40)
+ --theme-bg: mix($c-green, $c-card-light, 10)
+
+.tag-plugin[color='cyan']
+ --theme: mix($c-cyan, $c-card-light, 40)
+ --theme-bg: mix($c-cyan, $c-card-light, 10)
+
+.tag-plugin[color='blue']
+ --theme: mix($c-blue, $c-card-light, 40)
+ --theme-bg: mix($c-blue, $c-card-light, 10)
+
+.tag-plugin[color='purple']
+ --theme: mix($c-purple, $c-card-light, 40)
+ --theme-bg: mix($c-purple, $c-card-light, 10)
+
+
+.tag-plugin[color='white']
+ --theme-bg: white
+
+
+.tag-plugin[color='dark']
+ --theme-bg: mix(black, $c-card-light, 75)
+ color: var(--card) !important
+
+body[theme='dark']
+ .tag-plugin[color='red']
+ --theme: mix($c-red, $c-card-dark, 40)
+ --theme-bg: mix($c-red, $c-card-dark, 10)
+
+ .tag-plugin[color='orange']
+ --theme: mix($c-orange, $c-card-dark, 40)
+ --theme-bg: mix($c-orange, $c-card-dark, 10)
+
+ .tag-plugin[color='yellow']
+ --theme: mix($c-yellow, $c-card-dark, 40)
+ --theme-bg: mix($c-yellow, $c-card-dark, 10)
+
+ .tag-plugin[color='green']
+ --theme: mix($c-green, $c-card-dark, 40)
+ --theme-bg: mix($c-green, $c-card-dark, 10)
+
+ .tag-plugin[color='cyan']
+ --theme: mix($c-cyan, $c-card-dark, 40)
+ --theme-bg: mix($c-cyan, $c-card-dark, 10)
+
+ .tag-plugin[color='blue']
+ --theme: mix($c-blue, $c-card-dark, 40)
+ --theme-bg: mix($c-blue, $c-card-dark, 10)
+
+ .tag-plugin[color='purple']
+ --theme: mix($c-purple, $c-card-dark, 40)
+ --theme-bg: mix($c-purple, $c-card-dark, 10)
+
+
+ .tag-plugin[color='white']
+ --theme-bg: white
+ color: var(--card)
+
+ .tag-plugin[color='dark']
+ --theme-bg: mix(black, $c-card-dark, 75)
+ color: var(--text-p0) !important
diff --git a/source/css/_layout/tag-plugins/folding.styl b/source/css/_layout/tag-plugins/folding.styl
new file mode 100644
index 0000000..23ead8f
--- /dev/null
+++ b/source/css/_layout/tag-plugins/folding.styl
@@ -0,0 +1,62 @@
+details
+ display: block
+ padding: 1rem
+ margin: 1rem 0
+ border-radius: $border-block
+ font-size: $fs15
+ background: var(--theme-bg)
+ border: 1px solid var(--theme)
+ summary
+ cursor: pointer
+ padding: 1rem
+ margin: 0 - 1rem
+ border-radius: $border-block
+ color: var(--text-p2)
+ font-size: $fs14
+ font-weight: 500
+ position: relative
+ line-height: normal
+ outline: none
+ >
+ p,h1,h2,h3,h4,h5,h6
+ display: inline
+ border-bottom: none !important
+ &:hover
+ color: var(--text-p0)
+ &:after
+ position: absolute
+ content: '+'
+ text-align: center
+ top: 50%
+ transform: translateY(-50%)
+ right: 1rem
+
+
+details[open]
+ >summary
+ border-bottom: 1px solid var(--theme)
+ border-bottom-left-radius: 0
+ border-bottom-right-radius: 0
+ color: var(--text-p1)
+ margin-bottom: 0
+ &:hover
+ &:after
+ content: '-'
+ >div.body
+ padding: 1rem
+ margin: 0 - 1rem
+ margin-top: 0
+ background: var(--card)
+ border-bottom-left-radius: $border-block
+ border-bottom-right-radius: $border-block
+ font-size: $fs15
+ inside-box()
+
+details[codeblock]>div.body
+ padding: 0
+ background: transparent
+ .highlight
+ border: none
+ border-radius: 0
+ background: transparent
+ margin: 0
diff --git a/source/css/_layout/tag-plugins/frame.styl b/source/css/_layout/tag-plugins/frame.styl
new file mode 100644
index 0000000..6cbfc19
--- /dev/null
+++ b/source/css/_layout/tag-plugins/frame.styl
@@ -0,0 +1,55 @@
+.md .frame-wrap
+ position: relative
+ overflow: hidden
+ margin: 0 auto
+ max-width: 100%
+ display: flex
+ flex-direction: column
+ align-items: center
+ img,video
+ border-radius: 0
+.md .frame-wrap .frame
+ z-index: 1
+ display: block
+ position: absolute;
+ background-size: 100%;
+ background-repeat: no-repeat;
+ overflow: hidden;
+
+.md .img-wrap .frame-wrap
+ &[focus]
+ height: auto
+
+.md .frame-wrap
+ iphone11
+ img,video
+ width: 287px
+ margin-top: 19px
+ margin-bottom: 20px
+ .frame
+ background-image: url(https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@3/img/frame/iphone11.svg);
+ width: 329px
+ height: 658px
+ &[focus='top']
+ img,video
+ margin-bottom: 0 !important
+ &:not([focus='bottom'])
+ .frame
+ top: 0
+ &[focus='bottom']
+ img,video
+ bottom: 0
+ margin-top: 0 !important
+ .frame
+ bottom: 0
+
+@media screen and (max-width: $device-mobile)
+ .md .frame-wrap
+ iphone11
+ img,video
+ width: 208px
+ margin-top: 13px
+ margin-bottom: 14px
+ .frame
+ width: 238px
+ height: 476px
diff --git a/source/css/_layout/tag-plugins/ghcard.styl b/source/css/_layout/tag-plugins/ghcard.styl
new file mode 100644
index 0000000..e3d30ef
--- /dev/null
+++ b/source/css/_layout/tag-plugins/ghcard.styl
@@ -0,0 +1,3 @@
+a.ghcard
+ display: inline-block
+ line-height: 0
diff --git a/source/css/_layout/tag-plugins/image.styl b/source/css/_layout/tag-plugins/image.styl
new file mode 100644
index 0000000..2c6f909
--- /dev/null
+++ b/source/css/_layout/tag-plugins/image.styl
@@ -0,0 +1,35 @@
+.img-wrap
+ margin-top: 1rem
+ margin-bottom: 1rem
+ .img-bg
+ line-height: 0
+ text-align: center
+ border-radius: 4px
+ img
+ display: inline-block
+ object-fit: contain
+ .image-meta
+ display: flex
+ justify-content: space-between
+ align-items: flex-start
+ padding-top: 0.5rem
+ .image-caption
+ display: inline-block
+ font-size: $fs-footnote
+ line-height: 1.4
+ color: var(--text-p3)
+ flex-grow: 1
+ &:empty
+ display: none
+ &.left
+ text-align: left
+ &.center
+ text-align: center
+ .image-download
+ margin-left: 4px
+ padding: 4px
+ line-height: 0
+ border-radius: 4px
+ trans2: color background
+ &:hover
+ background: var(--hover-block)
diff --git a/source/css/_layout/tag-plugins/link.styl b/source/css/_layout/tag-plugins/link.styl
new file mode 100644
index 0000000..3d1b33c
--- /dev/null
+++ b/source/css/_layout/tag-plugins/link.styl
@@ -0,0 +1,70 @@
+.md .tag.link
+ margin-top: 1em
+ margin-bottom: 1em
+
+.md .link-card
+ margin-right: 1em
+ background: var(--card)
+ display: inline-flex
+ justify-content: space-between
+ align-items: center
+ cursor: pointer
+ min-width 250px
+ max-width: 100%
+ box-shadow: $boxshadow-card
+ transition: box-shadow
+ @media screen and (max-width: $device-mobile-l)
+ max-width: 100%
+ width: 100%
+ border-radius: 8px
+ &:hover
+ box-shadow: $boxshadow-float, $boxshadow-card-float
+
+
+.md .link-card
+ div.left,div.right
+ pointer-events: none
+ div.right
+ width: 54px
+ height: 54px
+ margin: 12px
+ overflow: hidden
+ flex-shrink: 0
+ position: relative
+ i
+ font-size: 32px
+ line-height: 48px
+ margin-left: 4px
+ img
+ display: block
+ position: absolute
+ border-radius: $border-card / 4
+ top: 50%
+ left: 50%
+ transform: translate(-50%, -50%)
+ div.left
+ overflow: hidden
+ margin-left: 16px
+ span
+ margin: 0
+ display: block
+ txt-ellipsis()
+ span.title
+ font-weight: 500
+ color: var(--text-p1)
+ font-size: $fs14
+ span.url
+ flex-shrink: 0
+ color: var(--text-p2)
+ font-size: $fs13
+
+.md .link-group
+ display: grid
+ grid-template-columns: 1fr 1fr
+ @media screen and (max-width: $device-mobile-l * 2)
+ grid-template-columns: 1fr
+ grid-gap: $gap
+ .tag.link
+ margin: 0
+ .link-card
+ width: 100%
diff --git a/source/css/_layout/tag-plugins/note.styl b/source/css/_layout/tag-plugins/note.styl
new file mode 100644
index 0000000..2b7b103
--- /dev/null
+++ b/source/css/_layout/tag-plugins/note.styl
@@ -0,0 +1,16 @@
+.md div.note
+ $border-block = 4px
+ position: relative
+ margin-top: 1rem
+ margin-bottom: 1rem
+ padding: 1rem
+ border-radius: $border-block
+ font-size: $fs15
+ background: var(--theme-bg)
+ border: 1px solid var(--theme)
+ color: var(--text-p1)
+ div.title
+ inside-box: $fs15
+ margin-bottom: 0.5rem
+ div.body
+ inside-box: $fs14
diff --git a/source/css/_layout/tag-plugins/site-card.styl b/source/css/_layout/tag-plugins/site-card.styl
new file mode 100644
index 0000000..efd6b7a
--- /dev/null
+++ b/source/css/_layout/tag-plugins/site-card.styl
@@ -0,0 +1,68 @@
+.site-card-group
+ display: grid
+ grid-gap: $gap $gap
+ grid-template-columns: repeat(auto-fill, "calc((100% - 3 * %s) / 4)" % $gap)
+ @media screen and (max-width: $device-laptop)
+ grid-template-columns: repeat(auto-fill, "calc((100% - 2 * %s) / 3)" % $gap)
+ @media screen and (max-width: $device-mobile)
+ grid-template-columns: repeat(auto-fill, "calc((100% - 1 * %s) / 2)" % $gap)
+ margin-bottom: 2rem
+
+.site-card-group .site-card
+ line-height: 1.2
+ disable-select()
+ color: var(--text-p1)
+ .img
+ width: 100%
+ height 120px
+ @media screen and (max-width: $device-mobile)
+ height 100px
+ overflow: hidden
+ border-radius: $border-card * 0.5
+ box-shadow: 0 1px 2px 0px rgba(0, 0, 0, 0.2)
+ background: var(--color-block)
+ img
+ width: 100%
+ height 100%
+ transition: transform 2s ease
+ object-fit: cover
+
+ .info
+ margin-top: $gap * 0.5
+ img
+ width: 32px
+ height: 32px
+ border-radius: 16px
+ float: left
+ margin-right: 8px
+ margin-top: 2px
+ span
+ display: block
+ .title
+ font-weight: 600
+ color: var(--text-p1)
+ font-size: $fs15
+ margin-top: 1px
+ color: var(--color-p)
+ display: -webkit-box
+ -webkit-box-orient: vertical
+ overflow: hidden
+ -webkit-line-clamp: 1
+
+ .desc
+ font-size: $fs12
+ color: var(--text-p2)
+ margin-top: 2px
+ word-wrap: break-word;
+ color: var(--color-meta)
+ display: -webkit-box
+ -webkit-box-orient: vertical
+ overflow: hidden
+ -webkit-line-clamp: 2
+ .img
+ trans1 box-shadow
+ &:hover
+ .img
+ box-shadow: $boxshadow-float, $boxshadow-card-float
+ .info .title
+ color: $color-hover
diff --git a/source/css/_layout/tag-plugins/timeline.styl b/source/css/_layout/tag-plugins/timeline.styl
new file mode 100644
index 0000000..cef2b55
--- /dev/null
+++ b/source/css/_layout/tag-plugins/timeline.styl
@@ -0,0 +1,82 @@
+div.timeline
+ position: relative
+ display: block
+ margin: 1rem 0
+ padding-top: 1rem
+ padding-bottom: 1rem
+ &:before
+ content: ''
+ z-index: 0
+ position: absolute
+ background: var(--block-border)
+ width: 2px
+ left: 16px
+ top: 1rem
+ bottom: 1rem
+ svg.top, svg.bottom
+ position: absolute
+ z-index: 1
+ fill: var(--block-border)
+ height 16px
+ width: 16px
+ margin-left: 9px
+ svg.top
+ margin-top: -1rem
+ svg.bottom
+ margin-bottom: 1rem
+
+div.timenode
+ position: relative
+ background: var(--card)
+ border: 1px solid var(--block-border)
+ border-radius: 6px
+ margin-top: 1rem
+ margin-bottom: 1rem
+ font-size: $fs14
+ .header
+ line-height: 1.2
+ background: var(--block)
+ border-top-left-radius: 5px
+ border-top-right-radius: 5px
+ border-bottom: 1px solid var(--block-border)
+ display: flex
+ justify-content: space-between
+ align-items: center
+ font-size: $fs14
+ span
+ font-weight: 500
+ color: var(--text-p1)
+ padding: 0.8rem 16px
+ a
+ visibility: hidden
+ margin-left: 4px
+ padding: 0.4rem 8px
+ margin-right: 8px
+ line-height: 0
+ border-radius: 4px
+ color: var(--text-p1)
+ &:hover
+ color: $color-link
+ .body
+ padding: 0.5rem 1rem
+ p,.highlight,ol,ul
+ margin: 0.5rem 0
+ li
+ font-size: $fs14
+
+div.timenode:hover
+ .header a
+ visibility: visible
+
+// div.timeline:hover
+// &:before
+// background: var(--block-border)
+// svg.top, svg.bottom
+// fill: var(--block-border)
+// div.timenode:hover
+// border-color: var(--block-border)
+// .header
+// background: var(--block-border)
+// border-color: var(--block-border)
+// a
+// visibility: visible
diff --git a/source/css/_plugins/index.styl b/source/css/_plugins/index.styl
new file mode 100644
index 0000000..3391e80
--- /dev/null
+++ b/source/css/_plugins/index.styl
@@ -0,0 +1,3 @@
+// 根据主题配置加载
+if hexo-config('plugins.swiper.enable')
+ @import 'swiper'
diff --git a/source/css/_plugins/swiper.styl b/source/css/_plugins/swiper.styl
new file mode 100644
index 0000000..53ab211
--- /dev/null
+++ b/source/css/_plugins/swiper.styl
@@ -0,0 +1,48 @@
+:root
+ --swiper-theme-color: $color-mac-cyan !important
+.swiper-container
+ width: 100%
+ border-radius: 4px
+.swiper-container:not(.swiper-container-initialized)
+ display: none
+div.swiper-slide
+ text-align: center
+ display: -webkit-box
+ display: -ms-flexbox
+ display: -webkit-flex
+ display: flex
+ align-self: center
+ -webkit-box-pack: center
+ -ms-flex-pack: center
+ -webkit-justify-content: center
+ justify-content: center
+ -webkit-box-align: center
+ -ms-flex-align: center
+ -webkit-align-items: center
+ align-items: center
+ width: 50%
+ img
+ border-radius: 4px
+
+.swiper-container[width='max'] div.swiper-slide
+ width: 100%
+
+.swiper-container[width='min'] div.swiper-slide
+ width: 25%
+
+.swiper-button-prev,.swiper-button-next
+ padding: 1rem 0.5rem
+ margin-top: -2rem !important
+ border-radius: 4px
+ background: alpha(white, 0.25)
+ trans1 background
+ --swiper-theme-color: black !important
+ @supports (backdrop-filter: blur(20px))
+ background: alpha(white, 0.5)
+ backdrop-filter: saturate(200%) blur(20px)
+ &:after
+ font-size: 1.2rem !important
+ font-weight: 700 !important
+ &:hover
+ background: white
+ --swiper-theme-color: $color-hover !important
diff --git a/source/css/main.styl b/source/css/main.styl
new file mode 100644
index 0000000..9f560a8
--- /dev/null
+++ b/source/css/main.styl
@@ -0,0 +1,16 @@
+// 常量
+@import '_defines/base'
+@import '_defines/const'
+
+// 自定义
+@import '_custom'
+
+// 含自定义参数的常量
+@import '_defines/theme'
+@import '_defines/func'
+
+// 布局
+@import '_layout/*'
+
+// 可选插件
+@import '_plugins/index'
diff --git a/source/js/issues.js b/source/js/issues.js
new file mode 100644
index 0000000..deb0def
--- /dev/null
+++ b/source/js/issues.js
@@ -0,0 +1,185 @@
+const IssuesAPI = {
+ requestIssuesAPI(url, callback, timeout) {
+ let retryTimes = 10;
+ function request() {
+ return new Promise((resolve, reject) => {
+ let status = 0; // 0 等待 1 完成 2 超时
+ let timer = setTimeout(() => {
+ if (status === 0) {
+ status = 2;
+ timer = null;
+ reject('请求超时');
+ if (retryTimes == 0) {
+ timeout();
+ }
+ }
+ }, 5000);
+ fetch(url).then(function(response) {
+ if (status !== 2) {
+ clearTimeout(timer);
+ resolve(response);
+ timer = null;
+ status = 1;
+ }
+ if (response.ok) {
+ return response.json();
+ }
+ throw new Error('Network response was not ok.');
+ }).then(function(data) {
+ retryTimes = 0;
+ callback(data);
+ }).catch(function(error) {
+ if (retryTimes > 0) {
+ retryTimes -= 1;
+ setTimeout(() => {
+ request();
+ }, 5000);
+ } else {
+ timeout();
+ }
+ });
+ });
+ }
+ request();
+ },
+ parseIssueStrToJson(str) {
+ let jsonStr = str.match(/```json[\s|\S]*```/);
+ if (jsonStr && jsonStr.length > 0) {
+ jsonStr = jsonStr[0];
+ }
+ if (jsonStr) {
+ jsonStr = jsonStr.split('```json')[1].split('```')[0];
+ if (jsonStr) {
+ return JSON.parse(jsonStr);
+ }
+ }
+ return undefined;
+ },
+ groupIssuesData(cfg, data) {
+ var groups = new Object();
+ if (data.length > 0) {
+ if (cfg.group != undefined) {
+ const arr = cfg.group.split('=');
+ if (arr.length > 1) {
+ const groupKey = arr[0];
+ let groupList = arr[1];
+ if (groupKey && groupList) {
+ groupList = groupList.split(',');
+ }
+ cfg.group = groupList;
+ for (i = 0; i < data.length; i++) {
+ const obj = this.parseIssueStrToJson(data[i].body);
+ if (obj && (groupKey in obj)) {
+ let tmp = obj[groupKey];
+ tmp = tmp.replace(', ', ',').split(',');
+ for (var j = 0; j < tmp.length; j++) {
+ if (groupList.includes(tmp[j])) {
+ let arr = groups[tmp[j]];
+ if (arr == undefined) {
+ arr = new Array();
+ }
+ arr.push(obj);
+ groups[tmp[j]] = arr;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ cfg.group = [''];
+ for (i = 0; i < data.length; i++) {
+ const obj = this.parseIssueStrToJson(data[i].body);
+ if (obj) {
+ let arr = groups[''];
+ if (arr == undefined) {
+ arr = new Array();
+ }
+ arr.push(obj);
+ groups[''] = arr;
+ }
+ }
+ }
+ }
+ return groups;
+ },
+ getIssuesAPIForSites(cfg) {
+ const el = $(cfg.el)[0];
+ $(el).append('');
+ this.requestIssuesAPI(cfg.api, function(data) {
+ $(el).find('.loading').remove();
+ const dt = IssuesAPI.groupIssuesData(cfg, data);
+ const groupTitles = Object.keys(dt);
+ cfg.group.forEach((groupTitle, i) => {
+ const issues = dt[groupTitle];
+ if (issues && issues.length > 0) {
+ if (groupTitle.length > 0) {
+ $(el).append('' + groupTitle + ' ');
+ } else if (name == '' && groupTitles.length > 1) {
+ $(el).append('' + '未分组' + ' ');
+ }
+ $(el).append('
');
+ // layout items
+ for (j = 0; j < issues.length; j++) {
+ const issue = issues[j];
+ let imgTag = '';
+ if (issue.screenshot && issue.screenshot.length > 0) {
+ imgTag = '';
+ } else {
+ imgTag = '
';
+ }
+ let infoTag = '';
+ if (issue.avatar && issue.avatar.length > 0) {
+ infoTag += '
';
+ }
+ infoTag += '
' + issue.title + ' ' + issue.description + ' ';
+ const cardTag = '';
+ $(el).find('.site-card-group.' + i).append(cardTag);
+ }
+ }
+ });
+ }, function() {
+ $(el).find('.loading i').remove();
+ $(el).find('.loading p').text('加载失败,请稍后重试。');
+ });
+ },
+ getIssuesAPIForTimeline(cfg) {
+ const el = $(cfg.el)[0];
+ $(el).append('');
+ this.requestIssuesAPI(cfg.api, function(data) {
+ $(el).find('.loading').remove();
+ if (data.length > 0) {
+ for (i = 0; i < data.length; i++) {
+ const a = ' ';
+ const header = '';
+ const body = '';
+ const tag = '' + header + body + '
';
+ $(el).append(tag);
+ }
+ }
+ }, function() {
+ $(el).find('.loading i').remove();
+ $(el).find('.loading p').text('加载失败,请稍后重试。');
+ });
+ },
+ request() {
+ const els = document.getElementsByClassName('issues-wrap');
+ for (var i = 0; i < els.length; i++) {
+ const el = els[i];
+ const api = el.getAttribute('api');
+ const group = el.getAttribute('group');
+ var cfg = new Object();
+ cfg.class = el.getAttribute('class');
+ cfg.el = el;
+ cfg.api = api;
+ cfg.group = group;
+ if (cfg.class.split(' ').includes('sites')) {
+ this.getIssuesAPIForSites(cfg);
+ } else if (cfg.class.split(' ').includes('timeline')) {
+ this.getIssuesAPIForTimeline(cfg);
+ }
+ }
+ }
+};
+$(function () {
+ IssuesAPI.request();
+});
\ No newline at end of file
diff --git a/source/js/main.js b/source/js/main.js
new file mode 100644
index 0000000..d12fcec
--- /dev/null
+++ b/source/js/main.js
@@ -0,0 +1,56 @@
+const l_body = document.querySelector('.l_body');
+
+// 弹出侧边栏
+function toggleSidebar() {
+ if (l_body) {
+ l_body.classList.add('mobile');
+ l_body.classList.toggle("sidebar");
+ }
+}
+
+function setToc() {
+ const scrollOffset = 32;
+ var segs = [];
+ $("article.md :header").each(function (idx, node) {
+ segs.push(node)
+ });
+ // 滚动
+ $(document, window).scroll(function(e) {
+ var scrollTop = $(this).scrollTop();
+ var topSeg = null
+ for (var idx in segs) {
+ var seg = $(segs[idx])
+ if (seg.offset().top > scrollTop + scrollOffset) {
+ continue
+ }
+ if (!topSeg) {
+ topSeg = seg
+ } else if (seg.offset().top >= topSeg.offset().top) {
+ topSeg = seg
+ }
+ }
+ if (topSeg) {
+ $("#toc a.toc-link").removeClass("active")
+ var link = "#" + topSeg.attr("id")
+ if (link != '#undefined') {
+ $('#toc a.toc-link[href="' + encodeURI(link) + '"]').addClass("active")
+ } else {
+ $('#toc a.toc-link:first').addClass("active")
+ }
+ }
+ });
+}
+
+function setSedebar() {
+ $("#toc a.toc-link").click(function(e) {
+ l_body.classList.remove("sidebar");
+ });
+ $(".social-wrap a.comment").click(function(e) {
+ l_body.classList.remove("sidebar");
+ });
+}
+
+$(function () {
+ setToc();
+ setSedebar();
+});
\ No newline at end of file