├── .gitignore
├── source
├── css
│ ├── _components
│ │ ├── widgets
│ │ │ ├── music.styl
│ │ │ ├── instantclick.styl
│ │ │ ├── search.styl
│ │ │ ├── toc_blog.styl
│ │ │ ├── related.styl
│ │ │ ├── tagcloud.styl
│ │ │ ├── recent.styl
│ │ │ ├── timeline.styl
│ │ │ ├── ghrepo.styl
│ │ │ ├── toc_wiki.styl
│ │ │ ├── widgets.styl
│ │ │ ├── toc_common.styl
│ │ │ └── ghuser.styl
│ │ ├── tag-plugins
│ │ │ ├── index.styl
│ │ │ ├── ghcard.styl
│ │ │ ├── emoji.styl
│ │ │ ├── mark.styl
│ │ │ ├── media.styl
│ │ │ ├── hashtag.styl
│ │ │ ├── navbar.styl
│ │ │ ├── copy.styl
│ │ │ ├── toc.styl
│ │ │ ├── split.styl
│ │ │ ├── inline-labels.styl
│ │ │ ├── image.styl
│ │ │ ├── poetry.styl
│ │ │ ├── note.styl
│ │ │ ├── frame.styl
│ │ │ ├── about.styl
│ │ │ ├── folders.styl
│ │ │ ├── friends.styl
│ │ │ ├── folding.styl
│ │ │ ├── quot.styl
│ │ │ ├── tabs.styl
│ │ │ └── link.styl
│ │ ├── index.styl
│ │ ├── darkmode.styl
│ │ ├── main.styl
│ │ ├── pages
│ │ │ ├── error.styl
│ │ │ └── archives.styl
│ │ ├── partial
│ │ │ ├── footer.styl
│ │ │ ├── bread-nav.styl
│ │ │ ├── article-footer.styl
│ │ │ ├── navbar.styl
│ │ │ ├── cover.styl
│ │ │ └── related.styl
│ │ ├── layout.styl
│ │ └── sidebar
│ │ │ ├── sidebar.styl
│ │ │ └── footer.styl
│ ├── _common
│ │ ├── control.styl
│ │ ├── image.styl
│ │ ├── pre.styl
│ │ ├── svg.styl
│ │ ├── input.styl
│ │ ├── span.styl
│ │ ├── cap.styl
│ │ ├── blockquote.styl
│ │ ├── toast.styl
│ │ ├── html.styl
│ │ ├── loading.styl
│ │ ├── title.styl
│ │ ├── blur.styl
│ │ ├── base.styl
│ │ ├── button.styl
│ │ └── device.styl
│ ├── _plugins
│ │ ├── scrollreveal.styl
│ │ ├── comments
│ │ │ ├── beaudar.styl
│ │ │ ├── utterances.styl
│ │ │ └── artalk.styl
│ │ ├── fancybox.styl
│ │ ├── copycode.styl
│ │ ├── lazyload.styl
│ │ ├── index.styl
│ │ ├── swiper.styl
│ │ └── search
│ │ │ └── local-search.styl
│ ├── main.styl
│ └── _defines
│ │ ├── const.styl
│ │ └── theme.styl
├── images
│ ├── sun-moon.svg
│ ├── moon-fill.svg
│ ├── sun-fill.svg
│ ├── email.svg
│ ├── link_icon.svg
│ ├── telegram.svg
│ ├── weibo.svg
│ └── wechat.svg
└── js
│ ├── check_outdated_browser.js
│ └── plugins
│ ├── copycode.js
│ └── linkcard.js
├── scripts
├── generators
│ ├── 404.js
│ ├── tags.js
│ ├── categories.js
│ └── wiki.js
├── helpers
│ ├── category_color.js
│ ├── scrollreveal.js
│ ├── utils.js
│ ├── stellar_info.js
│ └── parse_config.js
├── filters
│ ├── index.js
│ ├── change_image.js
│ ├── img_onerror.js
│ └── img_lazyload.js
├── tags
│ ├── lib
│ │ ├── bvideo.js
│ │ ├── mark.js
│ │ ├── icon.js
│ │ ├── checkbox.js
│ │ ├── folding.js
│ │ ├── border.js
│ │ ├── emoji.js
│ │ ├── note.js
│ │ ├── swiper.js
│ │ ├── read
│ │ │ └── reel.js
│ │ ├── poetry.js
│ │ ├── split.js
│ │ ├── ghcard.js
│ │ ├── navbar.js
│ │ ├── folders.js
│ │ ├── about.js
│ │ ├── quot.js
│ │ ├── hashtag.js
│ │ ├── copy.js
│ │ ├── friends.js
│ │ ├── frame.js
│ │ ├── tabs.js
│ │ ├── toc.js
│ │ └── sites.js
│ └── inline-labels.js
└── events
│ ├── index.js
│ └── lib
│ └── config.js
├── _data
├── links.yml
└── widgets.yml
├── layout
├── components
│ ├── main
│ │ ├── header.jsx
│ │ ├── post_list
│ │ │ ├── paginator.jsx
│ │ │ └── wiki_card.jsx
│ │ ├── navbar
│ │ │ ├── list_wiki.jsx
│ │ │ └── list_categories.jsx
│ │ └── article
│ │ │ └── read_next.jsx
│ ├── cover
│ │ ├── cover.jsx
│ │ ├── post_cover.jsx
│ │ └── wiki_cover.jsx
│ ├── widgets
│ │ ├── markdown.jsx
│ │ ├── timeline.jsx
│ │ ├── tagcloud.jsx
│ │ ├── ghissues.jsx
│ │ ├── search.jsx
│ │ └── related.jsx
│ ├── plugins
│ │ ├── comments
│ │ │ ├── script.jsx
│ │ │ ├── twikoo
│ │ │ │ ├── layout.jsx
│ │ │ │ └── script.jsx
│ │ │ ├── waline
│ │ │ │ ├── layout.jsx
│ │ │ │ └── script.jsx
│ │ │ ├── artalk
│ │ │ │ ├── layout.jsx
│ │ │ │ └── script.jsx
│ │ │ ├── giscus
│ │ │ │ ├── script.jsx
│ │ │ │ └── layout.jsx
│ │ │ ├── beaudar
│ │ │ │ ├── script.jsx
│ │ │ │ └── layout.jsx
│ │ │ ├── utterances
│ │ │ │ ├── script.jsx
│ │ │ │ └── layout.jsx
│ │ │ └── layout.jsx
│ │ ├── katex
│ │ │ └── script.jsx
│ │ ├── mathjax
│ │ │ └── script.jsx
│ │ └── mermaid
│ │ │ └── script.jsx
│ ├── sidebar
│ │ ├── menu.jsx
│ │ ├── title.jsx
│ │ └── header.jsx
│ └── menu_button.jsx
├── 404.jsx
├── layout.jsx
├── tags.jsx
├── categories.jsx
├── post.jsx
├── page.jsx
├── wiki.jsx
└── archive.jsx
├── .prettierrc
├── package.json
├── LICENSE
├── languages
├── zh-CN.yml
├── zh-TW.yml
└── en.yml
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
--------------------------------------------------------------------------------
/source/css/_components/widgets/music.styl:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/index.styl:
--------------------------------------------------------------------------------
1 | @import 'read/*'
--------------------------------------------------------------------------------
/source/css/_common/control.styl:
--------------------------------------------------------------------------------
1 | .dis-select
2 | disable-select()
3 |
--------------------------------------------------------------------------------
/source/css/_plugins/scrollreveal.styl:
--------------------------------------------------------------------------------
1 | .reveal
2 | visibility: hidden
--------------------------------------------------------------------------------
/source/css/_components/widgets/instantclick.styl:
--------------------------------------------------------------------------------
1 | div#instantclick-bar
2 | background: var(--theme-highlight);
3 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/search.styl:
--------------------------------------------------------------------------------
1 | .widgets .widget-wrapper.search
2 | margin-top: 0
3 | margin-bottom: 0
4 |
--------------------------------------------------------------------------------
/source/css/_plugins/comments/beaudar.styl:
--------------------------------------------------------------------------------
1 | .cmt-body.beaudar
2 | margin: 0 -4px
3 | width: 'calc(100% + %s)' % 8px
4 |
--------------------------------------------------------------------------------
/source/css/_plugins/comments/utterances.styl:
--------------------------------------------------------------------------------
1 | .cmt-body.utterances
2 | margin: 0 -4px
3 | width: 'calc(100% + %s)' % 8px
4 |
--------------------------------------------------------------------------------
/source/css/_plugins/fancybox.styl:
--------------------------------------------------------------------------------
1 | img[fancybox='true']
2 | cursor: zoom-in
3 |
4 | .swiper-slide
5 | cursor: zoom-in
6 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/ghcard.styl:
--------------------------------------------------------------------------------
1 | .tag-plugin.ghcard
2 | line-height: 0
3 | a.ghcard
4 | display: inline-block
5 |
--------------------------------------------------------------------------------
/source/css/_common/image.styl:
--------------------------------------------------------------------------------
1 | div.lazy.img
2 | width: 100%
3 | height: 100%
4 | background-position: center
5 | background-size: cover
6 |
--------------------------------------------------------------------------------
/source/css/_common/pre.styl:
--------------------------------------------------------------------------------
1 | pre:not([class]):has(>code)
2 | display: block
3 | padding: 1rem
4 | overflow: auto
5 | code
6 | padding: 0
--------------------------------------------------------------------------------
/source/css/_common/svg.styl:
--------------------------------------------------------------------------------
1 | svg.icon
2 | width: 1em
3 | height: 1em
4 | vertical-align: middle
5 | fill: currentColor
6 | overflow: hidden
7 |
--------------------------------------------------------------------------------
/source/css/_components/index.styl:
--------------------------------------------------------------------------------
1 | @import 'partial/*'
2 | @import 'tag-plugins/*'
3 | @import 'sidebar/*'
4 | @import 'widgets/*'
5 | @import 'pages/*'
6 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/toc_blog.styl:
--------------------------------------------------------------------------------
1 | .widget-wrapper.toc.single .doc-tree
2 | &.active>.toc
3 | border-left: 2px solid var(--block-hover)
4 |
--------------------------------------------------------------------------------
/source/css/_plugins/comments/artalk.styl:
--------------------------------------------------------------------------------
1 | .cmt-body.artalk .artalk
2 | --at-color-main: $color-theme
3 | .atk-main-editor
4 | border-radius: $border-block
--------------------------------------------------------------------------------
/source/css/_common/input.styl:
--------------------------------------------------------------------------------
1 | input
2 | background: none
3 | border: none
4 |
5 | input.copy-area
6 | display: block
7 | font-family: $ff-code
8 | font-size: $fs-12
9 | font-weight: 700
10 | color: var(--text-p3)
11 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/emoji.styl:
--------------------------------------------------------------------------------
1 | .md-text .tag-plugin.emoji
2 | display: inline-block
3 | margin: -4px 2px 0
4 | vertical-align: middle
5 | img
6 | display: block
7 | object-fit: contain
8 | height: 1.75em
9 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/related.styl:
--------------------------------------------------------------------------------
1 | .widget-wrapper.related .widget-body a
2 | margin-top: 0.5rem
3 | margin-bottom: 1rem
4 | .title
5 | font-weight: 700
6 | font-size: $fs-14
7 | .excerpt
8 | -webkit-line-clamp: 3
9 |
--------------------------------------------------------------------------------
/source/css/_common/span.styl:
--------------------------------------------------------------------------------
1 | span.dot,span.sep
2 | font-size: 0.9em
3 | margin: 0 .25em
4 | span.dot:before
5 | content: '·'
6 | font-weight: 900
7 | span.sep:before
8 | content: '/'
9 | padding-left: 2px
10 | padding-right: 2px
11 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/tagcloud.styl:
--------------------------------------------------------------------------------
1 | .widget-wrapper.tagcloud .widget-body
2 | margin-top: 0.25rem
3 | a
4 | word-break: break-word
5 | color: var(--text-p2)
6 | line-height: 1.5
7 | &:hover
8 | color: $color-hover
9 |
--------------------------------------------------------------------------------
/scripts/generators/404.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 404 v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | */
4 |
5 | hexo.extend.generator.register('404', function (locals) {
6 | return {
7 | path: '/404.html',
8 | layout: ['404'],
9 | }
10 | })
11 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/mark.styl:
--------------------------------------------------------------------------------
1 | .md-text .tag-plugin.mark
2 | padding: 0 1px
3 | border-radius: 2px
4 | background: var(--theme-bg)
5 | border: 1px solid var(--theme-border)
6 | color: var(--text-p0)
7 | &[color=dark]
8 | border-color: var(--theme-bg)
9 |
--------------------------------------------------------------------------------
/_data/links.yml:
--------------------------------------------------------------------------------
1 | # 不要直接在这里写,要复制到 blog/source/_data/links.yml 位置
2 | '示例':
3 | - title: XAOXUU
4 | avatar: https://bu.dusays.com/2021/09/24/2f74810ceb3d3.png
5 | url: https://xaoxuu.com
6 | screenshot: https://bu.dusays.com/2022/10/23/63542895cfd29.png
7 | description:
8 |
--------------------------------------------------------------------------------
/source/css/_common/cap.styl:
--------------------------------------------------------------------------------
1 | .cap
2 | font-weight: 500
3 | font-size: $fs-12
4 | scrollbar-width: none
5 | color: var(--text-p3)
6 | &.blue
7 | color: darken($color-md-blue, 6)
8 | &.cyan
9 | color: darken($color-mac-cyan, 6)
10 | &.theme
11 | color: var(--theme-cap)
12 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/media.styl:
--------------------------------------------------------------------------------
1 | .tag-plugin.video
2 | line-height: 0
3 | margin: auto
4 | video, iframe
5 | border-radius: $border-card
6 | box-shadow: $boxshadow-card-float
7 | z-index 1
8 | background: var(--block)
9 | width: 100%
10 | height: 100%
11 | margin: 0
--------------------------------------------------------------------------------
/scripts/helpers/category_color.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | hexo.extend.helper.register('category_color', function (cat) {
4 | const cfg = hexo.theme.config
5 | if (cfg.article.category_color && cfg.article.category_color[cat]) {
6 | return { color: cfg.article.category_color[cat] }
7 | }
8 | return {}
9 | })
10 |
--------------------------------------------------------------------------------
/source/css/main.styl:
--------------------------------------------------------------------------------
1 | // 无需修改的常量
2 | @import '_defines/const'
3 |
4 | // 自定义参数
5 | @import '_custom'
6 |
7 | // 含自定义参数的常量以及函数
8 | @import '_defines/theme'
9 | @import '_defines/func'
10 |
11 | // 通用组件
12 | @import '_common/*'
13 | // 布局
14 | @import '_components/*'
15 |
16 | // 可选插件
17 | @import '_plugins/index'
18 |
--------------------------------------------------------------------------------
/source/images/sun-moon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/css/_components/darkmode.styl:
--------------------------------------------------------------------------------
1 | .darkmode-switch
2 | display: none
3 | cursor: pointer
4 | :root[data-theme="dark"] #darkmode-switch-auto
5 | display: inline-block
6 | :root[data-theme="auto"] #darkmode-switch-light
7 | display: inline-block
8 | :root[data-theme="light"] #darkmode-switch-dark
9 | display: inline-block
--------------------------------------------------------------------------------
/source/css/_common/blockquote.styl:
--------------------------------------------------------------------------------
1 | blockquote
2 | display: block
3 | margin-left: 0
4 | margin-right: 0
5 | padding: 1rem
6 | background: var(--card)
7 | border: 1px solid var(--card-border)
8 | border-left: 4px solid var(--theme-highlight)
9 | border-radius: $border-block
10 | box-shadow: $boxshadow-card
11 | color: var(--text-p2)
12 |
--------------------------------------------------------------------------------
/layout/components/main/header.jsx:
--------------------------------------------------------------------------------
1 | const SidebarHeader = require('../sidebar/header.jsx')
2 | const Header = (props) => {
3 | const { page, partial } = props
4 | if (page.header !== 'auto' && page.header !== false) {
5 | return
6 | } else {
7 | return <>>
8 | }
9 | }
10 |
11 | module.exports = Header
12 |
--------------------------------------------------------------------------------
/scripts/filters/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | hexo.extend.filter.register(
4 | 'after_render:html',
5 | require('./img_lazyload').processSite
6 | )
7 | hexo.extend.filter.register(
8 | 'after_render:html',
9 | require('./img_onerror').processSite
10 | )
11 | hexo.extend.filter.register(
12 | 'before_post_render',
13 | require('./change_image').changeImage,
14 | 9
15 | )
16 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/recent.styl:
--------------------------------------------------------------------------------
1 | .widget-wrapper.recent .widget-body
2 | display: flex
3 | flex-direction: column
4 | align-items: flex-start
5 | margin-top: 0.25rem
6 | a
7 | line-height: 1.2
8 | font-size: $fs-13
9 | margin: 0.25rem 0
10 | .title
11 | font-size: $fs-13
12 | color: var(--text-p2)
13 | &:hover
14 | color: $color-hover
15 |
16 |
--------------------------------------------------------------------------------
/scripts/generators/tags.js:
--------------------------------------------------------------------------------
1 | /**
2 | * tags v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | */
4 |
5 | hexo.extend.generator.register('tags', function (locals) {
6 | if (locals.tags && locals.tags.length > 0) {
7 | return {
8 | path: hexo.config.tag_dir + '/index.html',
9 | data: locals.posts,
10 | layout: ['tags'],
11 | }
12 | } else {
13 | return {}
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/source/css/_common/toast.styl:
--------------------------------------------------------------------------------
1 | div.toast
2 | max-width: 60%
3 | padding: 1rem 3rem
4 | line-height: 1.5
5 | color: var(--text-p1)
6 | font-weight: 500
7 | text-align: center
8 | border-radius: $border-card
9 | background: var(--card)
10 | position: fixed
11 | top: 50%
12 | left: 50%
13 | transform: translate(-50%, -50%)
14 | z-index: 9
15 | disable-select()
16 | box-shadow: $boxshadow-float
17 |
--------------------------------------------------------------------------------
/layout/components/cover/cover.jsx:
--------------------------------------------------------------------------------
1 | const PostCover = require('./post_cover.jsx')
2 | const WikiCover = require('./wiki_cover.jsx')
3 |
4 | const Cover = (props) => {
5 | const { page } = props
6 | switch (page.layout) {
7 | case 'post':
8 | return
9 | case 'wiki':
10 | return
11 | default:
12 | return <>>
13 | }
14 | }
15 |
16 | module.exports = Cover
17 |
--------------------------------------------------------------------------------
/scripts/filters/change_image.js:
--------------------------------------------------------------------------------
1 | /**
2 | * change_image.js v1 | https://github.com/chiyuki0325/hexo-theme-stellaris/
3 | *
4 | */
5 |
6 | 'use strict'
7 |
8 | module.exports.changeImage = function (data) {
9 | if (this.theme.config.tag_plugins.image.parse_markdown) {
10 | data.content = data.content.replace(
11 | /!\[(.*?)\]\((.*?)\s*(?:"(.*?)")?\)/g,
12 | '{% image $2 $3 %}'
13 | )
14 | }
15 | return data
16 | }
17 |
--------------------------------------------------------------------------------
/scripts/generators/categories.js:
--------------------------------------------------------------------------------
1 | /**
2 | * categories v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | */
4 |
5 | hexo.extend.generator.register('categories', function (locals) {
6 | if (locals.categories && locals.categories.length > 0) {
7 | return {
8 | path: hexo.config.category_dir + '/index.html',
9 | data: locals.posts,
10 | layout: ['categories'],
11 | }
12 | } else {
13 | return {}
14 | }
15 | })
16 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "jsxSingleQuote": true,
5 | "trailingComma": "es5",
6 | "tabWidth": 2,
7 | "useTabs": false,
8 | "arrowParens": "always",
9 | "printWidth": 80,
10 | "bracketSpacing": true,
11 | "jsxBracketSameLine": false,
12 | "endOfLine": "lf",
13 | "overrides": [
14 | {
15 | "files": "*.jsx",
16 | "options": {
17 | "parser": "babel"
18 | }
19 | }
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/scripts/tags/lib/bvideo.js:
--------------------------------------------------------------------------------
1 | /* bvideo.js v3 | https://github.com/chiyuki0325/hexo-theme-stellaris
2 | * https://github.com/MaxChang3/hexo-bilibili-card
3 | * {% bvideo v_id %}
4 | */
5 |
6 | 'use strict'
7 |
8 | module.exports = (ctx) =>
9 | function (args) {
10 | const { v_id } = ctx.args.map(args, [], ['v_id'])
11 | return `
`
13 | }
14 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/timeline.styl:
--------------------------------------------------------------------------------
1 | .widget-wrapper.timeline
2 | .widget-body
3 | margin-top: 0.5rem
4 | .tag-plugin.timeline .timenode
5 | .header
6 | margin-bottom: 0.5rem
7 | txt-ellipsis()
8 | .user-info
9 | background: var(--block)
10 | &:hover
11 | background: $color-hover
12 | color: var(--card)
13 | &:before
14 | display: none
15 | &+.timenode
16 | margin-top: 0.75rem
17 |
--------------------------------------------------------------------------------
/scripts/events/index.js:
--------------------------------------------------------------------------------
1 | /* global hexo */
2 |
3 | 'use strict'
4 |
5 | hexo.on('generateBefore', () => {
6 | // Merge config.
7 | require('./lib/config')(hexo)
8 | require('./lib/doc_tree')(hexo)
9 | require('./lib/utils')(hexo)
10 | })
11 |
12 | hexo.on('ready', () => {
13 | const { version, homepage, repository } = require('../../package.json')
14 | hexo.log.info(`Welcome to Stellaris ${version}
15 | DOCS ${homepage}
16 | REPO ${repository.url}
17 | `)
18 | })
19 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/hashtag.styl:
--------------------------------------------------------------------------------
1 | .md-text .tag-plugin.hashtag
2 | padding: 0px 8px
3 | border-radius: 100px
4 | background: var(--theme-block)
5 | color: var(--text-p2)
6 | margin: 2px 0
7 | display: inline-flex
8 | align-items: center
9 | --fsp: $fsp2
10 | font-size: var(--fsp)
11 | font-weight: 500
12 | trans2 background color
13 | span
14 | margin: 0 2px
15 | &:hover
16 | background: var(--text-p2)
17 | color: var(--theme-block)
18 |
--------------------------------------------------------------------------------
/scripts/helpers/scrollreveal.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | hexo.extend.helper.register('scroll_reveal', function (args) {
4 | const cfg = hexo.theme.config
5 | if (cfg.plugins.scrollreveal && cfg.plugins.scrollreveal.enabled) {
6 | return ' reveal'
7 | }
8 | return ''
9 | })
10 |
11 | // 兼容
12 |
13 | hexo.extend.helper.register('scrollreveal', function (args) {
14 | const cfg = hexo.theme.config
15 | if (cfg.plugins.scrollreveal && cfg.plugins.scrollreveal.enabled) {
16 | return ' reveal'
17 | }
18 | return ''
19 | })
20 |
--------------------------------------------------------------------------------
/source/css/_common/html.styl:
--------------------------------------------------------------------------------
1 | *
2 | outline: none
3 | html
4 | color-scheme: var(--color-scheme)
5 | font-family: $ff-body
6 | font-size: $fs-root
7 | -webkit-text-size-adjust: 100%
8 | -ms-text-size-adjust: 100%
9 | if hexo-config('style.smooth_scroll')
10 | scroll-behavior: smooth
11 | body
12 | background: var(--site-bg)
13 | margin: 0
14 | -webkit-font-smoothing: antialiased
15 | -moz-osx-font-smoothing: grayscale
16 | text-rendering: optimizelegibility
17 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0)
18 |
--------------------------------------------------------------------------------
/source/css/_components/main.styl:
--------------------------------------------------------------------------------
1 | .l_main
2 | position: relative
3 | padding-bottom: "calc(1 * %s)" % var(--gap-l)
4 | @media screen and (min-width: 1400px)
5 | margin-left: "calc(2 * %s)" % var(--gap-l)
6 | margin-right: "calc(2 * %s + %s / 2)" % (var(--gap-l) var(--width-left))
7 | @media screen and (min-width: $device-mobile-max)
8 | padding-top: "calc(2 * %s)" % var(--gap-l)
9 | @media screen and (max-width: $device-mobile-max)
10 | padding-top: 1rem
11 | header
12 | margin: 2rem 1rem 1rem
13 | .logo-wrap
14 | margin: 0
15 |
--------------------------------------------------------------------------------
/layout/components/main/post_list/paginator.jsx:
--------------------------------------------------------------------------------
1 | const Paginator = (props) => {
2 | const { paginator, page, is_home } = props
3 | if (is_home() && page.total > 1) {
4 | return (
5 |
15 | )
16 | } else {
17 | return <>>
18 | }
19 | }
20 |
21 | module.exports = Paginator
22 |
--------------------------------------------------------------------------------
/scripts/helpers/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * utils v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | */
4 |
5 | 'use strict'
6 |
7 | hexo.extend.helper.register('get_page', function (id) {
8 | const pages = hexo.locals.get('pages')
9 | let page = pages.data.find((element) => element._id == id)
10 | if (page && page._id == id) {
11 | return page
12 | } else {
13 | const posts = hexo.locals.get('posts')
14 | let post = posts.data.find((element) => element._id == id)
15 | if (post && post._id == id) {
16 | return post
17 | }
18 | }
19 | return null
20 | })
21 |
--------------------------------------------------------------------------------
/source/css/_plugins/copycode.styl:
--------------------------------------------------------------------------------
1 | .highlight
2 | position: relative
3 |
4 | .highlight .code .copy-btn
5 | position: absolute
6 | top: 0
7 | right: 0
8 | padding: 4px 0.5rem
9 | opacity: 0
10 | font-weight: 700
11 | color: var(--theme)
12 | cursor: pointer
13 |
14 | .highlight:hover .copy-btn
15 | opacity 0.25
16 |
17 | .highlight .code .copy-btn:hover
18 | color: var(--theme)
19 | opacity: 0.75
20 |
21 | .highlight .code .copy-btn.success
22 | color: $c-green
23 | opacity: 0.75
24 |
25 | .highlight .code .copy-btn.warning
26 | color: $c-orange
27 | opacity: 0.75
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/navbar.styl:
--------------------------------------------------------------------------------
1 | .md-text .tag-plugin.navbar
2 | text-align: center
3 | nav.cap
4 | margin: 0
5 | padding: 1px
6 | background: var(--card)
7 | box-shadow: $boxshadow-button
8 | border-radius: 6px
9 | display: inline-flex
10 | max-width: 100%
11 | a
12 | margin: 1px
13 | padding: 0.25em 0.75rem
14 | color: var(--text-p2)
15 | &:after
16 | display: none
17 | &:hover
18 | background: var(--block)
19 | &.active
20 | background: var(--block)
21 | box-shadow: none
22 | color: var(--text-p1)
--------------------------------------------------------------------------------
/layout/components/widgets/markdown.jsx:
--------------------------------------------------------------------------------
1 | const Markdown = (props) => {
2 | const { markdown } = props
3 | if (!props.content?.length) return <>>
4 | return (
5 |
6 | {props.title?.length > 0 && (
7 |
8 | {props.title}
9 |
10 | )}
11 |
15 |
16 | )
17 | }
18 |
19 | module.exports = Markdown
20 |
--------------------------------------------------------------------------------
/source/css/_components/pages/error.styl:
--------------------------------------------------------------------------------
1 | .md-text.error-page
2 | text-align: center
3 | margin-top: 2rem
4 | img#error
5 | width: 30vw
6 | max-height: 150px
7 | margin-bottom: 2rem
8 | h1
9 | font-size: 4rem
10 | margin-bottom: 0
11 | p.what,p.why
12 | margin: 0.5em
13 | p.why
14 | font-size: $fs-13
15 | a#back
16 | margin: 2rem 0
17 | display: inline-block
18 | background: #FDB62F
19 | color: black
20 | border-radius: 4px
21 | border: 2px solid black
22 |
23 | @media screen and (max-width: $device-tablet)
24 | .md-text.error-page
25 | margin-top: 4rem
26 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/script.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 |
3 | const CommentsScript = (props) => {
4 | const commentService = props.theme.comments.service
5 | if (commentService === false || commentService === null) {
6 | return
7 | } else {
8 | try {
9 | const CommentScript = require(`./${commentService}/script.jsx`)
10 | return
11 | } catch (e) {
12 | if (e.code === 'MODULE_NOT_FOUND') {
13 | return
14 | } else {
15 | throw e
16 | }
17 | }
18 | }
19 | }
20 |
21 | module.exports = CommentsScript
22 |
--------------------------------------------------------------------------------
/layout/components/plugins/katex/script.jsx:
--------------------------------------------------------------------------------
1 | const KatexScripts = (props) => {
2 | const { theme } = props
3 | const { plugins } = theme
4 | const { katex } = plugins
5 | const { enabled, css } = katex
6 |
7 | if (enabled) {
8 | const loadMathStylesheet = `
9 | function loadKatex() {
10 | console.log('KaTeX enabled')
11 | stellar.loadCSS('${css}')
12 | }
13 | loadKatex()
14 | `
15 | return (
16 | <>
17 |
18 | >
19 | )
20 | } else {
21 | return <>>
22 | }
23 | }
24 |
25 | module.exports = KatexScripts
26 |
--------------------------------------------------------------------------------
/scripts/tags/lib/mark.js:
--------------------------------------------------------------------------------
1 | /**
2 | * mark.js v1.0 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% mark text [color:color] %}
6 | *
7 | */
8 |
9 | 'use strict'
10 |
11 | module.exports = (ctx) =>
12 | function (args) {
13 | args = ctx.args.map(args, ['color'], ['text'])
14 | if (args.color == null) {
15 | args.color = ctx.theme.config.tag_plugins.mark.default_color
16 | }
17 | var el = ''
18 | el += ''
21 | el += args.text
22 | el += ' '
23 | return el
24 | }
25 |
--------------------------------------------------------------------------------
/scripts/helpers/stellar_info.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | hexo.extend.helper.register('stellar_info', function (args) {
4 | const repo = 'https://github.com/chiyuki0325/hexo-theme-stellaris'
5 | const wiki = 'https://xaoxuu.com/wiki/stellar/'
6 | const issues = repo + '/issues/'
7 | const { version } = require('../../package.json')
8 | const cfg = hexo.theme.config.stellar
9 | if (!args) {
10 | return repo
11 | } else if (args == 'name') {
12 | return 'Stellaris'
13 | } else if (args == 'version') {
14 | return version
15 | } else if (args == 'issues') {
16 | return repo + '/issues/'
17 | } else if (args == 'tree') {
18 | return repo + '/tree/' + version
19 | }
20 | return ''
21 | })
22 |
--------------------------------------------------------------------------------
/source/js/check_outdated_browser.js:
--------------------------------------------------------------------------------
1 | function checkOutdatedBrowser() {
2 | 'use strict'
3 |
4 | if ('ActiveXObject' in window) {
5 | return false
6 | }
7 |
8 | if (typeof Symbol == 'undefined') return false
9 | try {
10 | eval('class Foo {}')
11 | eval('var bar = (x) => x+1')
12 | } catch (e) {
13 | return false
14 | }
15 |
16 | return true
17 | }
18 |
19 | if (checkOutdatedBrowser() === false) {
20 | document.getElementById('start').innerHTML =
21 | '喔唷! 你的浏览器太老了,无法正常浏览本站。 请升级你的浏览器。
支持的浏览器版本如下:
Chrome 58+ Firefox 52+ Edge 14+ Opera 45+ Safari 10+ '
22 | }
23 |
--------------------------------------------------------------------------------
/source/images/moon-fill.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/scripts/tags/lib/icon.js:
--------------------------------------------------------------------------------
1 | /**
2 | * icon.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% icon source [height:1.75em] %}
6 | *
7 | */
8 |
9 | 'use strict'
10 |
11 | module.exports = (ctx) =>
12 | function (args) {
13 | args = ctx.args.map(args, ['height'], ['source'])
14 | var el = ''
15 | if (args.source == undefined) {
16 | return el
17 | }
18 | el += ''
19 | if (args.source) {
20 | el += ' '
26 | }
27 | el += ' '
28 | return el
29 | }
30 |
--------------------------------------------------------------------------------
/layout/components/sidebar/menu.jsx:
--------------------------------------------------------------------------------
1 | const SidebarMenu = (props) => {
2 | const { theme, md_text, md_link, url_for, page, __, where } = props
3 | return (
4 |
7 | {Object.keys(theme.sidebar.menu).map((id) => {
8 | const item = theme.sidebar.menu[id]
9 | if (!item || item.length === 0) {
10 | return <>>
11 | }
12 | return (
13 |
18 | {__(md_text(item))}
19 |
20 | )
21 | })}
22 |
23 | )
24 | }
25 |
26 | module.exports = SidebarMenu
27 |
--------------------------------------------------------------------------------
/source/css/_common/loading.styl:
--------------------------------------------------------------------------------
1 | svg.loading
2 | display: block
3 | position: absolute
4 | color: var(--text-p3)
5 | z-index: -1
6 | width: 100%
7 | height: 2rem
8 | margin: auto
9 | animation: spin infinite 2s
10 | animation-timing-function: linear
11 | @keyframes spin
12 | from
13 | transform:rotate(0deg)
14 | to
15 | transform:rotate(360deg)
16 |
17 | .loading-wrap
18 | margin: 0
19 | text-align: center
20 | background: var(--block)
21 | border-radius: $border-card
22 | position relative
23 | padding: 2rem
24 | svg
25 | margin: 4px
26 | &:after
27 | content: hexo-config('style.loading.loading')
28 | color: var(--text-p1)
29 | display: block
30 | font-size: 14px
31 | &.error
32 | &:after
33 | content: hexo-config('style.loading.error')
--------------------------------------------------------------------------------
/source/css/_common/title.styl:
--------------------------------------------------------------------------------
1 | h1,.h1
2 | font-size: $fs-h1
3 | font-weight: 700
4 | @media screen and (max-width: $device-mobile)
5 | font-size: $fs-h1
6 | h2,.h2
7 | font-size: $fs-h2
8 | h3,.h3
9 | font-size: $fs-h3
10 | h4,.h4
11 | font-size: $fs-h4
12 | h5
13 | font-size: $fs-h5
14 | h6
15 | font-size: $fs-h6
16 |
17 | // 次级段落字号
18 | .fs14
19 | font-size: $fs-14
20 | p
21 | font-size: $fs-14 !important
22 | li
23 | font-size: $fs-14 !important
24 | // 脚标字号
25 | .fs12
26 | font-size: $fs-12
27 |
28 | .widgets
29 | .post-title
30 | margin: 0.75rem 0
31 | line-height: 1.2
32 | display: block
33 | txt-ellipsis()
34 | .cap
35 | margin-bottom: 2px
36 | opacity: 0.5
37 | a
38 | color: inherit
39 | font-weight: 500
40 | &:hover
41 | color: $color-hover
42 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/twikoo/layout.jsx:
--------------------------------------------------------------------------------
1 | const TwikooLayout = (props) => {
2 | const { page } = props
3 | return (
4 |
26 | )
27 | }
28 |
29 | module.exports = TwikooLayout
30 |
--------------------------------------------------------------------------------
/source/css/_components/partial/footer.styl:
--------------------------------------------------------------------------------
1 | .page-footer
2 | margin: 4rem 1rem 3rem
3 | color: var(--text-p3)
4 | a
5 | color: var(--text-p3)
6 | &:hover
7 | color: $color-hover
8 |
9 | .page-footer .sitemap
10 | margin: .5rem 0 2rem
11 | display: grid
12 | scrollbar-width: none
13 | grid-gap: 1rem 1rem
14 | grid-auto-flow: column dense
15 | overflow: scroll
16 | scrollbar(0, 0)
17 |
18 |
19 | .page-footer .sitemap .sitemap-group
20 | display: flex
21 | flex-direction: column
22 | align-items: flex-start
23 | >span,>a
24 | text-decoration: none
25 | txt-ellipsis()
26 | >span
27 | margin: .5rem 0
28 | font-weight: 500
29 | color: var(--text-p1)
30 | >a
31 | margin: .2rem 0
32 |
33 |
34 | .page-footer .text
35 | p
36 | margin: 4px 0
37 | line-height: 1.5
38 | a:not([class])
39 | text-decoration: underline
40 | font-weight: 500
--------------------------------------------------------------------------------
/source/css/_plugins/lazyload.styl:
--------------------------------------------------------------------------------
1 | trans-cover($p, $t = 0.28s)
2 | trans2pro: transform 0.5s $p $t
3 | trans-site($p)
4 | trans2: box-shadow transform $p
5 | trans-user($p)
6 | trans3: box-shadow transform $p
7 |
8 | if hexo-config('plugins.lazyload.transition') == 'blur'
9 | img.lazy
10 | trans-cover filter
11 | &:not(.loaded)
12 | filter blur(8px)
13 | -webkit-filter blur(8px)
14 | &.loaded,&.error
15 | filter none
16 | -webkit-filter none
17 | .group-body .site-card .card-link>img
18 | trans-site filter
19 | .group-body .user-card .card-link>img
20 | trans-user filter
21 | else
22 | img.lazy
23 | trans-cover opacity 0.5s
24 | &:not(.loaded)
25 | opacity: 0
26 | &.loaded,&.error
27 | opacity: 1
28 | .group-body .site-card .card-link>img
29 | trans-site opacity
30 | .group-body .user-card .card-link>img
31 | trans-user opacity
32 |
--------------------------------------------------------------------------------
/source/images/sun-fill.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/waline/layout.jsx:
--------------------------------------------------------------------------------
1 | const WalineLayout = (props) => {
2 | const { page } = props
3 | return (
4 |
27 | )
28 | }
29 |
30 | module.exports = WalineLayout
31 |
--------------------------------------------------------------------------------
/layout/components/sidebar/title.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const HeaderTitle = (props) => {
3 | const { main, url, sub, url_for } = props
4 | return (
5 |
6 |
7 | {main}
8 |
9 | {sub &&
10 | (() => {
11 | let arr = sub.split('|')
12 | if (arr.length > 1) {
13 | return (
14 |
15 | {arr.shift().trim()}
16 |
17 | {arr.join('|')}
18 |
19 |
20 | )
21 | } else {
22 | return {sub}
23 | }
24 | })()}
25 |
26 | )
27 | }
28 |
29 | module.exports = HeaderTitle
30 |
--------------------------------------------------------------------------------
/scripts/filters/img_onerror.js:
--------------------------------------------------------------------------------
1 | /**
2 | * img_onerror.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 发现这个和 img_lazyload 有点冲突,会被 img_lazyload 覆盖
4 | */
5 |
6 | 'use strict'
7 |
8 | const fs = require('hexo-fs')
9 |
10 | module.exports.processSite = function (htmlContent) {
11 | const default_image = this.theme.config.default.image
12 | return htmlContent.replace(/ /gi, function (imgTag) {
13 | if (/="data:image(.*?)/gi.test(imgTag)) {
14 | return imgTag
15 | }
16 | if (/onerror/gi.test(imgTag)) {
17 | return imgTag
18 | }
19 | if (imgTag.includes(' no-lazy ') == false) {
20 | return imgTag
21 | }
22 | return (
23 | imgTag.slice(0, imgTag.length - 1) +
24 | " onerror=\"javascript:this.classList.add('error');this.src='" +
25 | default_image +
26 | '\';"' +
27 | imgTag.slice(imgTag.length - 1)
28 | )
29 | })
30 | }
31 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/artalk/layout.jsx:
--------------------------------------------------------------------------------
1 | module.exports = function (props) {
2 | const { page } = props
3 | let cfg = {}
4 | if (page.comment_id) {
5 | cfg['comment_id'] = page.comment_id
6 | }
7 | return (
8 | <>
9 |
28 | >
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/layout/404.jsx:
--------------------------------------------------------------------------------
1 | const Comments = require('./components/plugins/comments/layout.jsx')
2 | const ErrorPage = (props) => {
3 | const { config, theme, url_for, __ } = props
4 | let page = props.page
5 | page.menu_id = '404'
6 | page.layout = '404'
7 | page.comment_title = ''
8 | page.header = 'auto'
9 | page.robots = 'none'
10 | return (
11 |
12 |
13 |
14 |
15 |
16 | {__('page.error.what')}
17 |
18 | {__('page.error.why')}
19 |
20 |
21 | {__('page.error.action')}
22 |
23 | {theme.comments.enabled_in_404 ? : <>>}
24 |
25 | )
26 | }
27 |
28 | module.exports = ErrorPage
29 |
--------------------------------------------------------------------------------
/source/css/_common/blur.styl:
--------------------------------------------------------------------------------
1 | :root
2 | --blur-px: 12px
3 | --blur-bg: alpha(white, .5)
4 | if hexo-config('style.darkmode') == 'auto'
5 | @media (prefers-color-scheme: dark)
6 | :root
7 | --blur-bg: alpha(black, .5)
8 | if hexo-config('style.darkmode') == 'always'
9 | :root
10 | --blur-bg: alpha(black, .5)
11 | if hexo-config('style.darkmode') == 'auto-switch'
12 | :root[data-theme="dark"]
13 | --blur-bg: alpha(black, .5)
14 | :root[data-theme="auto"]
15 | @media (prefers-color-scheme: dark)
16 | --blur-bg: alpha(black, .5)
17 |
18 |
19 | .blur
20 | background: var(--blur-bg)
21 | @supports ((-webkit-backdrop-filter:blur(var(--blur-px))) or (backdrop-filter:blur(var(--blur-px))))
22 | background: var(--blur-bg) !important
23 | backdrop-filter: saturate(200%) blur(var(--blur-px))
24 | -webkit-backdrop-filter: saturate(200%) blur(var(--blur-px))
25 | &:hover
26 | background: var(--card)
27 |
--------------------------------------------------------------------------------
/source/css/_plugins/index.styl:
--------------------------------------------------------------------------------
1 | // 根据主题配置加载
2 | if hexo-config('plugins.lazyload.enabled')
3 | @import 'lazyload'
4 | if hexo-config('plugins.swiper.enabled')
5 | @import 'swiper'
6 | if hexo-config('plugins.scrollreveal.enabled')
7 | @import 'scrollreveal'
8 | if hexo-config('plugins.fancybox.enabled')
9 | @import 'fancybox'
10 | if hexo-config('plugins.copycode.enabled')
11 | @import 'copycode'
12 |
13 | // 搜索
14 | if hexo-config('search.service') == 'local_search'
15 | @import 'search/local-search'
16 |
17 | // 评论
18 | if hexo-config('comments.service') == 'beaudar'
19 | @import 'comments/beaudar'
20 | if hexo-config('comments.service') == 'twikoo'
21 | @import 'comments/twikoo'
22 | if hexo-config('comments.service') == 'utterances'
23 | @import 'comments/utterances'
24 | if hexo-config('comments.service') == 'waline'
25 | @import 'comments/waline'
26 | if hexo-config('comments.service') == 'artalk'
27 | @import 'comments/artalk'
28 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/copy.styl:
--------------------------------------------------------------------------------
1 | .md-text .tag-plugin.copy
2 | display: flex
3 | justify-content: space-between
4 | box-sizing: border-box
5 | background: var(--block)
6 | border-radius: $border-block
7 | border: 1px solid var(--block-border)
8 | overflow: hidden
9 | width: 320px
10 | max-width: 100%
11 | &[width='max']
12 | width: 100%
13 | @media screen and (max-width: $device-mobile-425)
14 | min-width: 100%
15 |
16 | input.copy-area
17 | display: inline-block
18 | padding: 0
19 | width: 100%
20 | color: var(--text-p2)
21 | line-height: 3
22 | text-indent: 1rem
23 | button.copy-btn
24 | margin: 0
25 | line-height: 3
26 | border-left: 1px solid var(--block-border)
27 | display: inline-block
28 | background: var(--block-hover)
29 | line-height: 0
30 | font-size: 1rem
31 | padding: 0 .75rem
32 | color: var(--text-p2)
33 | &:hover
34 | background: var(--card)
35 |
--------------------------------------------------------------------------------
/scripts/helpers/parse_config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * md_link(theme.sidebar.menu['home']) is '/'
3 | * md_text(theme.sidebar.menu['home']) is 'Home'
4 | */
5 |
6 | 'use strict'
7 |
8 | hexo.extend.helper.register('md_text', function (args) {
9 | if (args == undefined) {
10 | return ''
11 | }
12 | let tmp = args.split('](')
13 | if (tmp.length > 1) {
14 | tmp = tmp[0]
15 | if (tmp.length > 1) {
16 | tmp = tmp.substring(1, tmp.length)
17 | }
18 | }
19 | if (tmp == 'config.title') {
20 | tmp = hexo.config.title
21 | } else if (tmp == 'config.avatar') {
22 | tmp = hexo.config.avatar
23 | }
24 | return tmp
25 | })
26 |
27 | hexo.extend.helper.register('md_link', function (args) {
28 | if (args == undefined) {
29 | return ''
30 | }
31 | let tmp = args.split('](')
32 | if (tmp.length > 1) {
33 | tmp = tmp[1]
34 | if (tmp.length > 1) {
35 | tmp = tmp.substring(0, tmp.length - 1)
36 | }
37 | }
38 | return tmp
39 | })
40 |
--------------------------------------------------------------------------------
/layout/components/widgets/timeline.jsx:
--------------------------------------------------------------------------------
1 | const Timeline = (props) => {
2 | const TimelineTitle = (props) => {
3 | if (props.title) {
4 | return (
5 |
6 | {props.title}
7 |
8 | )
9 | }
10 | }
11 | if (props.api === undefined) {
12 | return <>>
13 | } else {
14 | return (
15 |
16 |
17 |
26 |
27 | )
28 | }
29 | }
30 |
31 | module.exports = Timeline
32 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/ghrepo.styl:
--------------------------------------------------------------------------------
1 | .widget-wrapper.ghrepo
2 | .repo
3 | display: block
4 | padding: 0.75rem 0.5rem
5 | color: var(--text-p2)
6 | background: var(--card)
7 | border-radius: $border-block
8 | box-shadow: $boxshadow-card
9 | trans2 box-shadow transform
10 | >div+div
11 | margin-top: 0.5rem
12 | span
13 | color: var(--text-p2)
14 | &:hover
15 | box-shadow: $boxshadow-card-float
16 | transform: translateY(-1px)
17 | svg
18 | margin-right: 4px
19 | .flex-row
20 | display: flex
21 | align-items: center
22 | .repo-name
23 | font-size: $fs-14
24 | font-weight: 700
25 | color: var(--text-p1)
26 | .repo-desc
27 | font-size: $fs-13
28 | margin-left: 2px
29 | margin-right: 2px
30 | .grid
31 | font-size: $fs-13
32 | display: grid
33 | grid-gap: 2px
34 | grid-template-columns: repeat(auto-fill, "calc((100% - 2 * %s) / 3)" % 2px)
35 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/toc.styl:
--------------------------------------------------------------------------------
1 | details.toc
2 | background: var(--block)
3 | border: 1px solid var(--block-border)
4 | border-radius: $border-block
5 | overflow: hidden
6 | color: var(--text-p2)
7 | summary
8 | cursor: pointer
9 | padding: 0 .5rem
10 | line-height: 2
11 | color: var(--text-p2)
12 | font-size: $fs-13
13 | font-weight: 500
14 | &:hover
15 | background: var(--block-hover)
16 |
17 | >div.body
18 | margin: var(--gap-p) 1rem 1rem
19 |
20 | details.toc[open] >summary
21 | border-bottom: 1px solid transparent
22 | &:hover
23 | border-bottom-color: var(--block-border)
24 |
25 | details.toc[open] >div.body
26 | section+section
27 | margin-top: 1rem
28 | section
29 | .header
30 | line-height: 2
31 | font-weight: 500
32 | .header+.doc_tree
33 | margin-top: 0
34 |
35 |
36 | @media screen and (max-width: $device-mobile-max)
37 | .tag-plugin.toc[display=mobile]
38 | display: block !important
39 |
--------------------------------------------------------------------------------
/scripts/generators/wiki.js:
--------------------------------------------------------------------------------
1 | /**
2 | * wiki v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | */
4 |
5 | hexo.extend.generator.register('wiki', function (locals) {
6 | var hasWiki = false
7 | locals.pages.forEach((page, i) => {
8 | if (page.layout == 'wiki') {
9 | hasWiki = true
10 | }
11 | })
12 | if (hasWiki) {
13 | var ret = []
14 | ret.push({
15 | path: (hexo.config.wiki_dir || 'wiki') + '/index.html',
16 | data: { filter: false },
17 | layout: ['wiki'],
18 | })
19 | if (hexo.theme.config.wiki && hexo.theme.config.wiki.all_tags) {
20 | for (let id of Object.keys(hexo.theme.config.wiki.all_tags)) {
21 | let tag = hexo.theme.config.wiki.all_tags[id]
22 | ret.push({
23 | path: tag.path,
24 | data: {
25 | filter: true,
26 | tagName: tag.name,
27 | },
28 | layout: ['wiki'],
29 | })
30 | }
31 | }
32 | return ret
33 | } else {
34 | return {}
35 | }
36 | })
37 |
--------------------------------------------------------------------------------
/source/css/_components/layout.styl:
--------------------------------------------------------------------------------
1 | .l_body
2 | display: flex
3 | margin: auto
4 | padding: 0 var(--gap-l)
5 | justify-content: center
6 |
7 | .l_body .l_left
8 | z-index: 8
9 | width: var(--width-left)
10 | flex-shrink: 0
11 | position: sticky
12 | position: -webkit-sticky
13 | top: 0
14 |
15 | .l_body .l_main
16 | flex-shrink: 1
17 | flex-grow: 1
18 | width: 320px
19 | max-width: var(--width-main)
20 |
21 |
22 | // 手机布局
23 | @media screen and (max-width: $device-mobile-max)
24 | .mobile-only
25 | display: block !important
26 | .l_body
27 | padding: 0
28 | .l_left
29 | position: fixed
30 | transform: translateX(-320px)
31 | margin: 0
32 | left: 0
33 | //background: var(--site-bg)
34 | box-shadow: $boxshadow-card-float
35 | z-index: 9
36 | .l_main
37 | max-width: 100%
38 | .l_body.mobile
39 | .l_left
40 | transition: transform .3s ease-out
41 | .l_body.mobile.sidebar
42 | .l_left
43 | transform: translateX(0px)
44 |
--------------------------------------------------------------------------------
/source/images/email.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/scripts/tags/lib/checkbox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * checkbox.js v2 | https://github.com/chiyuki0325/hexo-theme-stellaris/
3 | * radio.js v2 | https://github.com/chiyuki0325/hexo-theme-stellaris/
4 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
5 | *
6 | * {% checkbox [checked:false] [color:cyan] [symbol:plus/minus/times] text %}
7 | * {% radio [checked:false] [color:cyan] text %}
8 | */
9 |
10 | 'use strict'
11 |
12 | module.exports = (ctx, type) =>
13 | function (args) {
14 | args = ctx.args.map(args, ['color', 'checked', 'symbol'], ['text'])
15 | var el = ''
16 | // div
17 | el += ''
20 | // input
21 | el += ' '
26 | // text
27 | el += '' + args.text + ' '
28 | // div
29 | el += '
'
30 | return el
31 | }
32 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/split.styl:
--------------------------------------------------------------------------------
1 | .tag-plugin.split
2 | display: grid
3 | grid-gap: 16px
4 | grid-template-columns: repeat(auto-fill, "calc((100% - 1 * %s) / 2)" % 16px)
5 | >.cell
6 | margin: 1rem 0
7 | p:first-child>strong:only-child
8 | font-size: $fs-h4
9 | >.cell>
10 | p
11 | line-height: 1.5
12 | :first-child
13 | margin-top: 0
14 | :last-child
15 | margin-bottom: 0
16 | p:first-child
17 | margin-top: -0.25em
18 | p:last-child
19 | margin-bottom: -0.25em
20 |
21 |
22 | .tag-plugin.split
23 | @media screen and (max-width: $device-tablet)
24 | display: block
25 |
26 | .tag-plugin.split
27 | &[bg]>.cell
28 | padding: 1rem
29 | border-radius: $border-card
30 | &[bg='block']>.cell
31 | background: var(--block)
32 | &[bg='card']>.cell
33 | background: var(--card)
34 | box-shadow: $boxshadow-card
35 | trans2 box-shadow transform
36 | &:hover
37 | transform: translateY(-1px)
38 | box-shadow: $boxshadow-card-float
--------------------------------------------------------------------------------
/scripts/tags/lib/folding.js:
--------------------------------------------------------------------------------
1 | /**
2 | * folding.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% folding [color:yellow] [child:codeblock] [open:false] title %}
6 | * body
7 | * {% endfolding %}
8 | */
9 |
10 | 'use strict'
11 |
12 | module.exports = (ctx) =>
13 | function (args, content) {
14 | args = ctx.args.map(args, ['color', 'child', 'open'], ['title'])
15 | var el = ''
16 | // header
17 | el += ''
23 | // summary
24 | el += '' + (args.title || '') + ' '
25 | // content
26 | el += ''
27 | el += ctx.render
28 | .renderSync({ text: content, engine: 'markdown' })
29 | .split('\n')
30 | .join('')
31 | el += '
'
32 |
33 | return el
34 | }
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hexo-theme-stellaris",
3 | "version": "2.3.5",
4 | "description": "Powerful, elegant and modern Hexo theme, forked from hexo-theme-stellar.",
5 | "main": "package.json",
6 | "scripts": {},
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/chiyuki0325/hexo-theme-stellaris.git"
10 | },
11 | "keywords": [
12 | "hexo",
13 | "theme",
14 | "stellar",
15 | "stellaris"
16 | ],
17 | "author": "[Kirikaze Chiyuki](https://github.com/chiyuki0325)",
18 | "email": "me@chyk.ink",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/chiyuki0325/hexo-theme-stellaris/issues/"
22 | },
23 | "dependencies": {
24 | "hexo-renderer-jsx": "^2.0.0",
25 | "hexo-renderer-stylus": "^3.0.0",
26 | "html-react-parser": "^5.2.2",
27 | "node-fetch": "^2.7.0",
28 | "react": "^19.0.0",
29 | "react-dom": "^19.0.0"
30 | },
31 | "homepage": "https://blog.chyk.ink/wiki/stellaris/",
32 | "devDependencies": {
33 | "prettier": "3.4.2"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/inline-labels.styl:
--------------------------------------------------------------------------------
1 | .md-text
2 | u
3 | text-decoration: none
4 | border-bottom: 2px solid $color-hover
5 | emp
6 | width: fit-content
7 | text-decoration: none
8 | border-bottom: 4px dotted $color-hover
9 | wavy
10 | text-decoration: underline wavy $color-hover
11 | del
12 | color: var(--text-p3)
13 | text-decoration: line-through var(--text-p3)
14 | kbd
15 | border-radius: 4px
16 | border: 1px solid var(--card-border)
17 | //border-bottom-width: 2px
18 | background: var(--card)
19 | padding: 2px 4px 1px 4px
20 | font-family: $ff-code
21 | font-weight: 550
22 | psw
23 | color: transparent
24 | background: #a1a1a1
25 | border-radius: 3px
26 | trans1 background
27 | margin: auto 2px
28 | &:hover
29 | color: inherit
30 | background: none
31 | sup,sub
32 | color: var(--theme)
33 | line-height: 1
34 | // font-weight: 700
35 | font-family: $ff-code
36 | h1,h2,h3,h4
37 | sup,sub
38 | font-size: .85rem
39 |
--------------------------------------------------------------------------------
/layout/components/widgets/tagcloud.jsx:
--------------------------------------------------------------------------------
1 | const TagCloud = (props) => {
2 | const { site, tagcloud } = props
3 | if (site.tags === undefined || site.tags.length === 0) {
4 | return <>>
5 | }
6 | const opts = {
7 | min_front: props.min_front || 12,
8 | max_front: props.max_front || 24,
9 | amount: props.amount || 100,
10 | orderby: props.orderby || 'count',
11 | order: props.order || 1,
12 | color: props.color || false,
13 | start_color: props.start_color,
14 | end_color: props.end_color,
15 | show_count: props.show_count || false,
16 | class: 'tag ',
17 | }
18 | return (
19 |
20 | {props.title && (
21 |
22 | {props.title}
23 |
24 | )}
25 |
29 |
30 | )
31 | }
32 |
33 | module.exports = TagCloud
34 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/image.styl:
--------------------------------------------------------------------------------
1 | .tag-plugin.image
2 | margin-top: 1rem
3 | margin-bottom: 1rem
4 | .image-bg
5 | text-align: center
6 | border-radius: $border-image
7 | position: relative
8 | overflow: hidden
9 | &:hover
10 | .image-download
11 | opacity: 1 !important
12 | img
13 | display: block
14 | object-fit: cover
15 | .image-download
16 | position: absolute
17 | bottom: 8px
18 | right: 8px
19 | font-size: 1.125rem
20 | padding: 6px
21 | line-height: 0
22 | border-radius: 4px
23 | trans3: color opacity background
24 | color: var(--text-p1)
25 | &:hover
26 | background: var(--card) !important
27 | color: $color-hover
28 | .image-meta
29 | display: flex
30 | justify-content: center
31 | padding: 0.5rem 0
32 | .image-caption
33 | display: inline-block
34 | font-size: $fs-12
35 | color: var(--text-p2)
36 | line-height: 1.5
37 | text-align: justify
38 | &:empty
39 | display: none
40 |
--------------------------------------------------------------------------------
/source/css/_components/partial/bread-nav.styl:
--------------------------------------------------------------------------------
1 | // 面包屑导航
2 | .bread-nav
3 | padding: 0.25rem 1rem 0
4 | color: var(--text-p3)
5 | font-weight: 500
6 | display: flex
7 | justify-content: space-between
8 | div#breadcrumb
9 | display: flex
10 | align-items: center
11 | flex-direction: row
12 | &:not([style]) a
13 | color: var(--theme-link)
14 | &[style] a
15 | color: inherit
16 | span.sep
17 | color: var(--text-p3)
18 | a.cap:hover
19 | color: $color-hover
20 | div#post-meta
21 | margin-top: 2px
22 |
23 |
24 | .bread-nav .ghrepo
25 | font-size: $fs-13
26 | display: flex
27 | flex-direction: column
28 | align-items: flex-start
29 | border-left: 1px solid var(--text-meta)
30 | padding-left: 8px
31 | a
32 | display: flex
33 | align-items: center
34 | color: var(--text-p2)
35 | svg
36 | margin-right: 4px
37 | &.bold
38 | font-weight: 600
39 | color: var(--text-p1)
40 | span
41 | margin-left: 4px
42 | &:hover
43 | color: var(--theme-link)
44 | a+a
45 | margin-top: 8px
46 |
--------------------------------------------------------------------------------
/source/css/_common/base.styl:
--------------------------------------------------------------------------------
1 | pre
2 | font-family: $ff-codeblock !important
3 | font-size: $fs-codeblock
4 | tab-size: 4
5 | -moz-tab-size: 4
6 | -o-tab-size: 4
7 | -webkit-tab-size: 4
8 |
9 | a
10 | text-decoration: none
11 | color: var(--theme-link)
12 | &:hover
13 | color: $color-hover
14 |
15 | .md-text p:not([class])
16 | text-align: convert(hexo-config('style.text-align'))
17 |
18 | hr
19 | color: var(--text-meta)
20 | opacity: .1
21 |
22 |
23 | img
24 | max-width: 100%
25 |
26 |
27 | ul,ol
28 | padding-left: 1.5rem
29 |
30 |
31 | // table
32 | table:not([class])
33 | border-collapse: collapse
34 | overflow: auto
35 | display: block
36 | margin: 1rem 0
37 | max-width: 100%
38 | vertical-align: text-top
39 | font-size: $fs-14
40 | th
41 | background: var(--block)
42 | td,th
43 | padding: 0.5em 1em
44 | border: 1px solid var(--block-hover)
45 | line-height: 1.5
46 | tr
47 | word-break: keep-all
48 | white-space:nowrap
49 | trans()
50 | &:hover
51 | background: var(--block)
52 |
53 | *[ff=title]
54 | font-family: $ff-logo
55 |
--------------------------------------------------------------------------------
/source/css/_components/sidebar/sidebar.styl:
--------------------------------------------------------------------------------
1 | .l_left
2 | display: flex
3 | flex-direction: column
4 | word-break: break-all
5 | text-align: justify
6 | height: 100vh
7 | background: var(--site-bg)
8 | .header
9 | margin: var(--gap-l) var(--gap-l) 0
10 | margin-top: "calc(2 * %s)" % var(--gap-l)
11 | @media screen and (min-width: $device-mobile-max)
12 | >.widgets:first-child>.widget-wrapper:first-child
13 | margin-top: "calc(2 * %s)" % var(--gap-l)
14 | @media screen and (max-width: $device-mobile-max)
15 | height: 100vh
16 |
17 |
18 | .l_left[layout=wiki]
19 | padding-bottom: 0
20 | height: 100vh
21 | @media screen and (max-width: $device-mobile-max)
22 | height: 100vh
23 | .widgets >:last-child
24 | margin-bottom: 6rem
25 |
26 |
27 | .l_left .widgets .widget-wrapper.logo-wrap.wiki
28 | margin-bottom: 1.5rem
29 | flex-direction: column
30 | align-items: flex-start
31 | a.wiki-home
32 | margin-bottom: 0.5rem
33 | color: var(--text-p1)
34 | svg
35 | margin-right: 2px
36 | &:hover
37 | color: $color-hover
38 | filter: unset !important
39 |
40 |
--------------------------------------------------------------------------------
/scripts/tags/lib/border.js:
--------------------------------------------------------------------------------
1 | /**
2 | * border.js v2.0 | https://github.com/chiyuki0325/hexo-theme-stellaris
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% border [color:color] [child:codeblock/tabs] title %}
6 | * body
7 | * {% endborder %}
8 | */
9 |
10 | 'use strict'
11 |
12 | module.exports = (ctx) =>
13 | function (args, content) {
14 | args = ctx.args.map(args, ['color', 'child'], ['title'])
15 | const { title } = args
16 | if (args.color == null) {
17 | args.color = ctx.theme.config.tag_plugins.note.default_color
18 | }
19 | var el = ''
20 | // header
21 | el += ''
24 | // title
25 | if (title && title.length > 0) {
26 | el += '
' + title + '
'
27 | }
28 | // content
29 | el += '
'
30 | el += ctx.render
31 | .renderSync({ text: content, engine: 'markdown' })
32 | .split('\n')
33 | .join('')
34 | el += '
'
35 |
36 | return el
37 | }
38 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Kirikaze Chiyuki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/source/images/link_icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/layout/layout.jsx:
--------------------------------------------------------------------------------
1 | const Layout = (props) => {
2 | const { page, body } = props
3 | const Head = require('./components/head.jsx')
4 | const Cover = require('./components/cover/cover.jsx')
5 | const Sidebar = require('./components/sidebar/sidebar.jsx')
6 | const Header = require('./components/main/header.jsx')
7 | const Scripts = require('./components/scripts.jsx')
8 | const Footer = require('./components/main/footer.jsx')
9 | const MenuButton = require('./components/menu_button.jsx')
10 | return (
11 |
12 |
14 |
15 |
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | module.exports = Layout
33 |
--------------------------------------------------------------------------------
/scripts/tags/lib/emoji.js:
--------------------------------------------------------------------------------
1 | /**
2 | * emoji.js v2 | https://github.com/chiyuki0325/hexo-theme-stellaris/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% emoji [source] name [height:1.75em] %}
6 | *
7 | */
8 |
9 | 'use strict'
10 |
11 | module.exports = (ctx) =>
12 | function (args) {
13 | const config = ctx.theme.config.tag_plugins.emoji
14 | args = ctx.args.map(args, ['height'], ['source', 'name'])
15 | var el = ''
16 | if (args.source == undefined) {
17 | return el
18 | }
19 | el += ''
20 | if (args.name == undefined) {
21 | // 省略了 source
22 | for (let id in config) {
23 | if (config[id]) {
24 | args.name = args.source
25 | args.source = id
26 | break
27 | }
28 | }
29 | }
30 | if (config[args.source] && args.name) {
31 | let url = config[args.source].replace('%s', args.name)
32 | el += ' '
38 | }
39 | el += ' '
40 | return el
41 | }
42 |
--------------------------------------------------------------------------------
/scripts/tags/lib/note.js:
--------------------------------------------------------------------------------
1 | /**
2 | * note.js v1.1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% note [color:color] [title] content %}
6 | */
7 |
8 | 'use strict'
9 |
10 | module.exports = (ctx) =>
11 | function (args) {
12 | args = ctx.args.map(args, ['color'], ['title', 'content'])
13 | if (args.content == undefined || args.content.length <= 0) {
14 | args.content = args.title
15 | args.title = ''
16 | }
17 | const { title } = args
18 | if (args.color == null) {
19 | args.color = ctx.theme.config.tag_plugins.note.default_color
20 | }
21 | var el = ''
22 | // header
23 | el += ''
26 | // title
27 | if (title && title.length > 0) {
28 | el += '
' + title + '
'
29 | }
30 | // content
31 | el += '
'
32 | el += ctx.render
33 | .renderSync({ text: args.content, engine: 'markdown' })
34 | .split('\n')
35 | .join('')
36 | el += '
'
37 |
38 | return el
39 | }
40 |
--------------------------------------------------------------------------------
/layout/tags.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const NavBarListPost = require('./components/main/navbar/list_post.jsx')
3 | const Tags = (props) => {
4 | const TagList = (props) => {
5 | const elements = []
6 | const { site, url_for } = props
7 | site.tags.sort('length', -1).each((tag) => {
8 | elements.push(
9 |
10 | {tag.name}
11 |
12 | )
13 | })
14 | return elements
15 | }
16 |
17 | const { site, scroll_reveal, __ } = props
18 | let { page } = props
19 | page.robots = 'noindex,follow'
20 | if (page.menu_id === undefined) {
21 | page.menu_id = 'post'
22 | }
23 | if (site.tags.length) {
24 | page.title = __('btn.tags')
25 | page.layout = 'tags'
26 | return (
27 |
28 |
29 |
34 |
35 | )
36 | } else {
37 | return <>>
38 | }
39 | }
40 |
41 | module.exports = Tags
42 |
--------------------------------------------------------------------------------
/source/images/telegram.svg:
--------------------------------------------------------------------------------
1 | Telegram_logo
--------------------------------------------------------------------------------
/layout/components/plugins/comments/twikoo/script.jsx:
--------------------------------------------------------------------------------
1 | const TwikooScript = (props) => {
2 | const { theme } = props
3 | const generatedScript = `
4 | function load_twikoo() {
5 | if (!document.querySelectorAll("#twikoo_container")[0]) return;
6 | stellar.loadScript('${theme.comments.twikoo.js}', {defer: true}).then(function () {
7 | const el = document.getElementById("twikoo_container");
8 | var path = el.getAttribute('comment_id');
9 | if (!path) {
10 | path = decodeURI(window.location.pathname);
11 | }
12 | twikoo.init(Object.assign(${JSON.stringify(theme.comments.twikoo)}, {
13 | el: '#twikoo_container',
14 | path: path,
15 | }));
16 | });
17 | }
18 | InstantClick.on('change', () => {
19 | load_twikoo();
20 | });
21 | window.addEventListener(
22 | "load", load_twikoo, false
23 | );
24 | `
25 | return (
26 |
30 | )
31 | }
32 |
33 | module.exports = TwikooScript
34 |
--------------------------------------------------------------------------------
/source/css/_common/button.styl:
--------------------------------------------------------------------------------
1 | button
2 | border: none
3 | font-weight: 500
4 | outline: none
5 | disable-select()
6 | cursor: pointer
7 |
8 | a.button
9 | font-weight: 500
10 | line-height: 1
11 | padding: 0.75rem 2rem
12 | border-radius: 4px
13 | trans1: background
14 | font-size: $fs-15
15 | -moz-box-align: center
16 | align-items: center
17 | box-sizing: border-box
18 | display: inline-flex
19 | -moz-box-pack: center
20 | justify-content: center
21 | text-decoration-line: none
22 | vertical-align: middle
23 | margin: 0px
24 | overflow: hidden
25 | border: 1px solid
26 | border-color: #d1d1d1
27 | outline-style: none
28 | padding: 5px 12px
29 | min-width: 96px
30 | font-weight: 600
31 | line-height: 20px
32 | transition-duration: 100ms
33 | transition-property: background-color, border-color, color;
34 | transition-timing-function: cubic-bezier(0.8,0,0.1,1)
35 | color: var(--text-revert);
36 | background-color: var(--theme-highlight);
37 | border-color: transparent;
38 | &.theme
39 | background: $color-theme
40 | color: var(--card)
41 | &:hover
42 | background-color: var(--theme-cap)
43 | border-color: #c7c7c7
44 |
45 | a[onclick]:hover
46 | cursor: pointer
47 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/toc_wiki.styl:
--------------------------------------------------------------------------------
1 | .widget-wrapper.toc.multi .widget-header
2 | color: var(--text-p1)
3 | font-size: $fs-14
4 |
5 |
6 | // 其它分页链接
7 | .widget-wrapper.toc.multi .doc-tree
8 | border-radius: $border-block
9 | background: var(--block)
10 | overflow: hidden
11 | border: 1px solid var(--block-border)
12 | a.doc-tree-link
13 | color: var(--text-p2)
14 | padding: 0.5rem
15 | display: block
16 | font-size: $fs-12
17 | font-weight: 500
18 | position: relative
19 | &:after
20 | position: absolute
21 | right: .5rem
22 | &.active
23 | color: var(--text-p1)
24 | &:only-child
25 | background: var(--card)
26 | &:hover
27 | background: var(--block-hover)
28 | &:after
29 | content: '+'
30 |
31 |
32 | // 当前分页链接
33 | .widget-wrapper.toc.multi .doc-tree.active
34 | a.doc-tree-link
35 | background: var(--block)
36 | font-weight: 700
37 | &:not(:only-child)
38 | border-bottom: 1px solid var(--block-border)
39 | &:hover:after
40 | content: none
41 | >.toc
42 | padding: 4px
43 | background: var(--card)
44 | a.toc-link:before
45 | left: -2px
46 | width: 4px
47 | a.toc-link:hover
48 | background: var(--block)
49 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/poetry.styl:
--------------------------------------------------------------------------------
1 | .md-text .tag-plugin.poetry
2 | align-self: center
3 | &[align=center]
4 | align-items: center
5 | >.title
6 | font-weight: 500
7 | font-size: 1rem
8 | margin-top: var(--gap-p)
9 | >.meta
10 | color: var(--text-p2)
11 | font-size: $fs-12
12 | font-weight: 500
13 | span+span
14 | margin-left: 4px
15 | >.body
16 | margin: var(--gap-p) 0
17 | border-top: 1px dashed var(--block-border)
18 | border-bottom: 1px dashed var(--block-border)
19 | p
20 | font-size: $fs-14
21 | >.footer
22 | font-style: italic
23 | color: var(--text-p4)
24 | margin: var(--gap-p) 0
25 | font-size: $fs-12
26 |
27 | // 描边修饰
28 | .md-text .tag-plugin.poetry
29 | padding-left: 1rem
30 | position: relative
31 | &:before
32 | content: ''
33 | position: absolute
34 | width: 4px
35 | left: -4px
36 | top: 4px
37 | bottom: 4px
38 | border-radius: 4px
39 | background: var(--block)
40 | >.title
41 | position: relative
42 | &:before
43 | content: ''
44 | position: absolute
45 | width: 4px
46 | left: calc(-1rem - 4px)
47 | top: 6px
48 | bottom: 6px
49 | border-radius: 4px
50 | background: $color-accent
51 |
--------------------------------------------------------------------------------
/layout/components/widgets/ghissues.jsx:
--------------------------------------------------------------------------------
1 | // 由 Copilot 转换而成,不保证可用性
2 |
3 | const GitHubIssues = (props) => {
4 | const { page, theme } = props
5 | let repo
6 | let branch = 'main'
7 | if (page.repo) {
8 | repo = page.repo
9 | } else if (page.layout === 'wiki' && page.wiki) {
10 | let proj = theme.wiki.tree[page.wiki]
11 | if (proj?.repo) {
12 | repo = proj.repo
13 | if (proj.branch !== undefined) {
14 | branch = proj.branch
15 | }
16 | }
17 | }
18 | if (repo === undefined) {
19 | return <>>
20 | }
21 | let api =
22 | theme.api_host.ghapi + '/repos/' + repo + '/issues?per_page=' + props.limit
23 | if (props.labels) {
24 | api += '&labels=' + props.labels
25 | }
26 | return (
27 |
28 | {props.title && (
29 |
30 | {props.title}
31 |
32 | )}
33 |
40 |
41 | )
42 | }
43 |
44 | module.exports = GitHubIssues
45 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/waline/script.jsx:
--------------------------------------------------------------------------------
1 | const WalineScript = (props) => {
2 | const { theme } = props
3 | const generatedScript = `
4 | function load_comment(){
5 | if(!document.getElementById("waline_container"))return;
6 | stellar.loadCSS('${theme.comments.waline.css}');
7 | stellar.loadScript('${theme.comments.waline.js}', {defer:true}).then(function () {
8 | const el = document.getElementById("waline_container");
9 | var path = el.getAttribute('comment_id');
10 | if (!path) {
11 | path = decodeURI(window.location.pathname);
12 | }
13 | if (!path.endsWith('/')) {
14 | path += '/';
15 | }
16 | Waline.init(Object.assign(${JSON.stringify(theme.comments.waline)}, {
17 | el: '#waline_container',
18 | path: path,
19 | }));
20 | });
21 | }
22 | InstantClick.on('change', () => {
23 | load_comment();
24 | });
25 | window.addEventListener(
26 | "load", load_comment, false
27 | );
28 | `
29 | return (
30 |
34 | )
35 | }
36 |
37 | module.exports = WalineScript
38 |
--------------------------------------------------------------------------------
/scripts/tags/lib/swiper.js:
--------------------------------------------------------------------------------
1 | /**
2 | * swiper.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% swiper [width:max] [effect:cards] %}
6 | * 
7 | * {% endswiper %}
8 | */
9 |
10 | 'use strict'
11 |
12 | module.exports = (ctx) =>
13 | function (args, content) {
14 | args = ctx.args.map(args, ['width', 'effect'])
15 | var el = ''
16 | function slide() {
17 | let imgs = ctx.render.renderSync({ text: content, engine: 'markdown' })
18 | imgs = imgs.match(/ /gi)
19 | if (imgs && imgs.length > 0) {
20 | imgs.forEach((img, i) => {
21 | img = img.replace(' ' + img + ''
23 | })
24 | }
25 | }
26 | el += ''
29 | el += '
'
30 | slide()
31 | el += '
'
32 | el += ''
33 | el += '
'
34 | el += '
'
35 | el += '
'
36 | return el
37 | }
38 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/giscus/script.jsx:
--------------------------------------------------------------------------------
1 | const GiscusScript = (props) => {
2 | const generatedScript = `
3 | function loadGiscus() {
4 | const els = document.querySelectorAll("#comments #giscus");
5 | if (els.length === 0) return;
6 | els.forEach((el, i) => {
7 | try {
8 | el.innerHTML = '';
9 | } catch (error) {
10 | console.error(error);
11 | }
12 | var script = document.createElement('script');
13 | script.src = 'https://giscus.app/client.js';
14 | script.async = true;
15 | for (let key of Object.keys(el.attributes)) {
16 | let attr = el.attributes[key];
17 | if (['class', 'id'].includes(attr.name) === false) {
18 | script.setAttribute(attr.name, attr.value);
19 | }
20 | }
21 | el.appendChild(script);
22 | });
23 | }
24 | InstantClick.on('change', () => {
25 | loadGiscus();
26 | });
27 | window.addEventListener(
28 | "load", loadGiscus, false
29 | );
30 | `
31 | return (
32 |
36 | )
37 | }
38 |
39 | module.exports = GiscusScript
40 |
--------------------------------------------------------------------------------
/source/css/_plugins/swiper.styl:
--------------------------------------------------------------------------------
1 | :root
2 | --swiper-theme-color: $color-theme !important
3 | .swiper-container
4 | width: 100%
5 | border-radius: 4px
6 | --gap-p: 2rem
7 | .swiper-container:not(.swiper-container-initialized)
8 | display: none
9 | div.swiper-slide
10 | text-align: center
11 | display: -webkit-box
12 | display: -ms-flexbox
13 | display: -webkit-flex
14 | display: flex
15 | align-self: center
16 | -webkit-box-pack: center
17 | -ms-flex-pack: center
18 | -webkit-justify-content: center
19 | justify-content: center
20 | -webkit-box-align: center
21 | -ms-flex-align: center
22 | -webkit-align-items: center
23 | align-items: center
24 | width: 50%
25 | img
26 | border-radius: 4px
27 |
28 | .swiper-container[width='max'] div.swiper-slide
29 | width: 100%
30 |
31 | .swiper-container[width='min'] div.swiper-slide
32 | width: 25%
33 |
34 | .swiper-button-prev,.swiper-button-next
35 | padding: 1rem 0.5rem
36 | margin-top: -2rem !important
37 | border-radius: 4px
38 | background: alpha(white, 0.25)
39 | trans1 background
40 | --swiper-theme-color: black !important
41 | &:after
42 | font-size: 1.2rem !important
43 | font-weight: 700 !important
44 | &:hover
45 | background: white !important
46 | --swiper-theme-color: $color-hover !important
47 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/beaudar/script.jsx:
--------------------------------------------------------------------------------
1 | const BeaudarScript = (props) => {
2 | const generatedScript = `
3 | function loadBeaudar() {
4 | const els = document.querySelectorAll("#comments #beaudar");
5 | if (els.length === 0) return;
6 | els.forEach((el, i) => {
7 | try {
8 | el.innerHTML = '';
9 | } catch (error) {
10 | console.error(error);
11 | }
12 | var script = document.createElement('script');
13 | script.src = 'https://beaudar.lipk.org/client.js';
14 | script.async = true;
15 | for (let key of Object.keys(el.attributes)) {
16 | let attr = el.attributes[key];
17 | if (['class', 'id'].includes(attr.name) === false) {
18 | script.setAttribute(attr.name, attr.value);
19 | }
20 | }
21 | el.appendChild(script);
22 | });
23 | }
24 | InstantClick.on('change', () => {
25 | loadBeaudar();
26 | });
27 | window.addEventListener(
28 | "load", loadBeaudar, false
29 | );
30 | `
31 | return (
32 |
36 | )
37 | }
38 |
39 | module.exports = BeaudarScript
40 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/artalk/script.jsx:
--------------------------------------------------------------------------------
1 | module.exports = function (prop) {
2 | const { config, theme, page } = props
3 | const generatedScript = `
4 | function load_artalk() {
5 | if (!document.querySelectorAll("#artalk_container")[0]) return
6 | stellar.loadCSS('${theme.comments.artalk.css}')
7 | stellar.loadScript('${theme.comments.artalk.js}', {defer: true}).then(function () {
8 | const el = document.getElementById("artalk_container")
9 | var path = el.getAttribute('comment_id')
10 | if (!path) {
11 | path = decodeURI(window.location.pathname)
12 | }
13 | var artalk = new Artalk({
14 | el: '#artalk_container',
15 | pageKey: path,
16 | pageTitle: '${page.title || page.seo_title}',
17 | server: '${theme.comments.artalk.server}',
18 | placeholder: '${theme.comments.artalk.placeholder}',
19 | site: '${config.title}',
20 | darkMode: '${theme.comments.artalk.darkMode}'
21 | })
22 | })
23 | }
24 | InstantClick.on('change', () => {
25 | load_artalk()
26 | });
27 | window.addEventListener(
28 | "load", load_artalk, false
29 | )`
30 | return (
31 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/utterances/script.jsx:
--------------------------------------------------------------------------------
1 | const UtterancesScript = (props) => {
2 | const generatedScript = `
3 | function loadUtterances() {
4 | const els = document.querySelectorAll("#comments #utterances");
5 | if (els.length === 0) return;
6 | els.forEach((el, i) => {
7 | try {
8 | el.innerHTML = '';
9 | } catch (error) {
10 | console.error(error);
11 | }
12 | var script = document.createElement('script');
13 | script.src = 'https://utteranc.es/client.js';
14 | script.async = true;
15 | for (let key of Object.keys(el.attributes)) {
16 | let attr = el.attributes[key];
17 | if (['class', 'id'].includes(attr.name) === false) {
18 | script.setAttribute(attr.name, attr.value);
19 | }
20 | }
21 | el.appendChild(script);
22 | });
23 | }
24 | InstantClick.on('change', () => {
25 | loadUtterances();
26 | });
27 | window.addEventListener(
28 | "load", loadUtterances, false
29 | );
30 | `
31 | return (
32 |
36 | )
37 | }
38 |
39 | module.exports = UtterancesScript
40 |
--------------------------------------------------------------------------------
/scripts/tags/lib/read/reel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * reel.js v1.1 | https://github.com/HcGys/stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * reel:
6 | * {% reel [title] [author:作者] [date:日期] [footer:footer] %}
7 | * body
8 | * {% endreel %}
9 | *
10 | */
11 |
12 | 'use strict'
13 |
14 | module.exports = (ctx) =>
15 | function (args, content) {
16 | var el = ''
17 | args = ctx.args.map(args, ['author', 'date', 'footer'], ['title'])
18 |
19 | el += ''
21 | el += '
'
22 | el += '
'
23 | if (args.title) el += args.title // 布局需要
24 | el += '
'
25 | if (args.author) {
26 | el += '
'
27 | if (args.author) {
28 | el += '' + args.author + ' '
29 | }
30 | el += '
'
31 | }
32 | el += '
'
33 | el += ctx.render
34 | .renderSync({ text: content, engine: 'markdown' })
35 | .split('\n')
36 | .join('')
37 | el += '
'
38 | if (args.date) {
39 | el += '
' + args.date + '
'
40 | }
41 | el += ''
44 | el += '
'
45 | el += '
'
46 | return el
47 | }
48 |
--------------------------------------------------------------------------------
/source/css/_defines/const.styl:
--------------------------------------------------------------------------------
1 | // 用来进行设备适配,请勿修改
2 | $device-mobile-375 = 375px
3 | $device-mobile-425 = 425px
4 | $device-mobile = 500px
5 | $device-mobile-max = 667px
6 | $device-tablet = 768px
7 | $device-laptop = 1024px
8 | $device-desktop = 1440px
9 | $device-2k = 2048px
10 | $device-4k = 2560px
11 |
12 | // 预定义颜色,不需要修改
13 | $color-md-red = #f44336
14 | $color-md-pink = #E91E63
15 | $color-md-purple = #9c27b0
16 | $color-md-deep-purple = #673ab7
17 | $color-md-indigo = #3f51b5
18 | $color-md-light-blue = #4BA7EE
19 | $color-md-blue = #2196f3
20 | $color-md-deep-blue = #3367d6
21 | $color-md-teal = #009688
22 | $color-md-green = #4caf50
23 | $color-md-light-green = #8bc34a
24 | $color-md-orange = #ff9800
25 | $color-md-deep-orange = #ff5722
26 | $color-md-brown = #795548
27 | $color-md-blue-grey = #607d8b
28 | $color-md-grey = #9e9e9e
29 | $color-md-light-grey =#e0e0e0
30 | $color-md-yellow = #FCEC60
31 | $color-md-amber = #F6C344
32 |
33 | $color-mac-cyan = #1BCDFC
34 | $color-mac-green = #3DC550
35 | $color-mac-yellow = #FFBD2B
36 | $color-mac-red = #FE5F58
37 |
38 | $color-google-blue = #4688F1
39 | $color-google-green = #3AA757
40 | $color-google-yellow = #FABB2D
41 | $color-google-red = #E8453C
42 |
43 | $c-red = #F44336
44 | $c-orange = #FA6400
45 | $c-yellow = #FFBD2B
46 | $c-green = #3DC550
47 | $c-cyan = #1BCDFC
48 | $c-blue = #2196f3
49 | $c-purple = #9c27b0
50 |
--------------------------------------------------------------------------------
/layout/components/main/navbar/list_wiki.jsx:
--------------------------------------------------------------------------------
1 | module.exports = function NavBarListWiki(props) {
2 | const { page, config, theme, url_for, __ } = props
3 | return (
4 |
5 |
6 | {/* 所有项目 */}
7 |
11 | {__('btn.all_wiki')}
12 |
13 | {/*项目分类*/}
14 | {(() => {
15 | const { shelf, all_tags } = theme.wiki
16 | const result = []
17 |
18 | for (let id of Object.keys(all_tags)) {
19 | let tag = all_tags[id]
20 | let projects = tag.items.filter((item) => shelf.includes(item))
21 | if (projects && projects.length > 0) {
22 | const isActive =
23 | tag.name && tag.name.length > 0 && page.tagName === tag.name
24 | result.push(
25 |
30 | {tag.name}
31 |
32 | )
33 | }
34 | }
35 |
36 | return result
37 | })()}
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/note.styl:
--------------------------------------------------------------------------------
1 | .md-text .tag-plugin.note
2 | position: relative
3 | margin-top: 1rem
4 | margin-bottom: 1rem
5 | padding: 0 1rem
6 | border-radius: $border-block
7 | background: var(--theme-bg)
8 | box-shadow: $boxshadow-card
9 | border: 1px solid var(--theme-border)
10 | color: var(--text-p1)
11 | >.title
12 | font-size: $fs-h4
13 | line-height: 1.2
14 | margin-top: 1rem
15 | >.body
16 | &,p
17 | font-size: $fs-15
18 | line-height: 1.5
19 | margin-top: .5rem
20 | margin-bottom: 1rem
21 | >.body:only-child
22 | margin: 1rem 0
23 |
24 |
25 | .md-text .tag-plugin.note[color]
26 | code
27 | background: none
28 | .md-text .tag-plugin.note:not([color])
29 | .highlight
30 | background: var(--block-hover)
31 | .md-text .tag-plugin.note[child=codeblock]
32 | padding: 0
33 | >.title, >.body:only-child
34 | margin-top: 0
35 | >.body
36 | margin-bottom: 0
37 | .highlight
38 | margin: 0
39 | border: none
40 | background: none
41 |
42 | .md-text .tag-plugin.note[child=tabs]
43 | >.body
44 | margin: 0
45 | >.tabs
46 | margin-top: .5rem
47 |
48 | .md-text .tag-plugin.note[child=iframe]
49 | overflow: hidden
50 | padding: 0
51 | >.body
52 | margin: 0
53 | iframe
54 | margin: 0
55 |
56 | .md-text .tag-plugin .tag-plugin.note
57 | --gap-p: 1rem
58 |
--------------------------------------------------------------------------------
/scripts/tags/lib/poetry.js:
--------------------------------------------------------------------------------
1 | /**
2 | * poetry.js v1.1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * poetry:
6 | * {% poetry [title] [author:作者] [date:日期] [footer:footer] %}
7 | * body
8 | * {% endpoetry %}
9 | *
10 | */
11 |
12 | 'use strict'
13 |
14 | module.exports = (ctx) =>
15 | function (args, content) {
16 | var el = ''
17 | args = ctx.args.map(args, ['author', 'date', 'footer'], ['title'])
18 |
19 | el += ''
21 | el += '
'
22 | if (args.title) {
23 | el += '
'
24 | el += args.title
25 | el += '
'
26 | }
27 | if (args.author || args.date) {
28 | el += '
'
29 | if (args.author) {
30 | el += '' + args.author + ' '
31 | }
32 | if (args.date) {
33 | el += '' + args.date + ' '
34 | }
35 | el += '
'
36 | }
37 | el += '
'
38 | el += ctx.render
39 | .renderSync({ text: content, engine: 'markdown' })
40 | .split('\n')
41 | .join('')
42 | el += '
'
43 | if (args.footer) {
44 | el += ''
47 | }
48 | el += '
'
49 | el += '
'
50 | return el
51 | }
52 |
--------------------------------------------------------------------------------
/source/images/weibo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/frame.styl:
--------------------------------------------------------------------------------
1 | .md-text .frame-wrap
2 | position: relative
3 | overflow: hidden
4 | margin: 0 auto
5 | max-width: 100%
6 | display: flex
7 | flex-direction: column
8 | align-items: center
9 | img,video
10 | border-radius: 0
11 | .md-text .frame-wrap .frame
12 | z-index: 1
13 | display: block
14 | position: absolute;
15 | background-size: 100%;
16 | background-repeat: no-repeat;
17 | overflow: hidden;
18 |
19 | .md-text .tag-plugin.img-wrap .frame-wrap
20 | &[focus]
21 | height: auto
22 |
23 | .md-text .frame-wrap
24 | iphone11
25 | img,video
26 | width: 287px
27 | margin-top: 19px
28 | margin-bottom: 20px
29 | .frame
30 | background-image: url(https://fastly.jsdelivr.net/gh/cdn-x/placeholder@1.0.1/frame/iphone11.svg);
31 | width: 329px
32 | height: 658px
33 | &[focus='top']
34 | img,video
35 | margin-bottom: 0 !important
36 | &:not([focus='bottom'])
37 | .frame
38 | top: 0
39 | &[focus='bottom']
40 | img,video
41 | bottom: 0
42 | margin-top: 0 !important
43 | .frame
44 | bottom: 0
45 |
46 | @media screen and (max-width: $device-mobile)
47 | .md-text .frame-wrap
48 | iphone11
49 | img,video
50 | width: 208px
51 | margin-top: 13px
52 | margin-bottom: 14px
53 | .frame
54 | width: 238px
55 | height: 476px
56 |
--------------------------------------------------------------------------------
/source/css/_components/partial/article-footer.styl:
--------------------------------------------------------------------------------
1 | .md-text .article-footer
2 | margin-top: 4rem
3 | padding: 1rem
4 | background: var(--block)
5 | border-radius: $border-block
6 | box-shadow: $boxshadow-card
7 | border: 1px solid var(--block-border)
8 | &:empty
9 | display: none
10 | .header
11 | font-weight: 500
12 | color: var(--text-p2)
13 | font-size: $fs-h4
14 | .body
15 | input.copy-area
16 | margin: 0.75rem 0
17 | padding: 0
18 | width: 100%
19 | p
20 | color: var(--text-p2)
21 | margin: 0.5em 0
22 | a
23 | font-weight: unset
24 | ul
25 | margin: 0
26 | overflow: hidden
27 | .post-title
28 | margin: 0.5rem 0
29 | line-height: 1.2
30 | word-break: break-all
31 | section+section
32 | margin-top: 1rem
33 | padding-top: 1rem
34 | border-top: 1px solid var(--block-border)
35 |
36 | .md-text .article-footer .social-wrap
37 | grid-gap: 0.5rem 1rem
38 | margin: 0
39 |
40 | .md-text .article-footer .qrcode
41 | width: 128px
42 | padding: 4px
43 | background: white
44 | border-radius: 4px
45 | box-shadow: 0 4px 8px 0px rgba(0, 0, 0, 0.1), 0 0 32px 0px rgba(0, 0, 0, 0.1)
46 | overflow: hidden
47 | trans1 height
48 | img
49 | object-fit: contain
50 |
51 | .md-text .article-footer .qrcode.display
52 | margin: 2rem auto 1rem
53 | height: 128px !important
54 | visibility: visible !important
55 |
--------------------------------------------------------------------------------
/layout/components/widgets/search.jsx:
--------------------------------------------------------------------------------
1 | const Search = (props) => {
2 | const { page, __ } = props
3 | let filter = ''
4 | if (props.filter === 'auto') {
5 | if (page.layout === 'wiki') {
6 | let matches = page.path.match(/(.*?)\/(.*?)\//i)
7 | if (matches?.length > 0) {
8 | filter = matches[0]
9 | }
10 | }
11 | } else {
12 | if (props.filter?.length > 0) {
13 | filter = props.filter
14 | }
15 | }
16 | let placeholder = ''
17 | if (filter.length > 0) {
18 | if (!filter.startsWith('/')) {
19 | filter = '/' + filter
20 | }
21 | placeholder = props.placeholder || __('search.search_in', filter)
22 | } else {
23 | placeholder = props.placeholder || __('search.search')
24 | }
25 |
26 | return (
27 |
28 |
29 |
30 |
39 |
40 |
{__('search.no_results')}
41 |
42 |
43 |
44 | )
45 | }
46 |
47 | module.exports = Search
48 |
--------------------------------------------------------------------------------
/languages/zh-CN.yml:
--------------------------------------------------------------------------------
1 | btn:
2 | home: 主页
3 | blog: 文章
4 | wiki: 项目
5 | recent_publish: 近期发布
6 | all_wiki: 所有项目
7 | category: 分类
8 | categories: 分类
9 | tag: 标签
10 | tags: 标签
11 | archives: 归档
12 | getting_started: 开始阅读
13 | search: 搜索
14 |
15 | meta:
16 | recent_update: 最近更新
17 | toc: 本文目录
18 | read_next: 接下来阅读
19 | prev: 回顾上一篇
20 | next: 接下来阅读
21 | older: 较早文章
22 | newer: 较新文章
23 | references: 参考资料
24 | related_posts: 您可能感兴趣的文章
25 | comment_title: 快来参与讨论吧
26 | back_to_top: 回到顶部
27 | more: '更多%s'
28 | created: 发布于
29 | updated: 更新于
30 | license: 许可协议
31 | share: 分享文章
32 | date_suffix:
33 | just: 刚刚
34 | min: 分钟前
35 | hour: 小时前
36 | day: 天前
37 | month: 个月前
38 |
39 | footer:
40 | license: '本博客所有文章除特别声明外,均采用 %s 许可协议,转载请注明出处。'
41 | info_not_open_source: '本站由 %s 创建,使用 %s 作为主题。'
42 | info_open_source: '本站由 %s 创建,使用 %s 作为主题,您可以在 %s 找到本站源码。'
43 | powered_by_1: '由 '
44 | powered_by_2: ' 强力驱动'
45 | server_render: '当前为服务端渲染模式,部分功能可能已被自动禁用。'
46 |
47 | page:
48 | error:
49 | what: 很抱歉,您访问的页面不存在
50 | why: 可能是输入地址有误或该地址已被删除
51 | action: 返回主页
52 |
53 | search:
54 | search: 站内搜索
55 | search_in: 在 %s 中搜索
56 | no_results: 没有找到内容!
57 |
58 | message:
59 | copied: 复制成功
60 | theme_switched:
61 | light: 切换到浅色模式
62 | dark: 切换到深色模式
63 | auto: 切换到跟随系统配色
64 |
65 | symbol:
66 | comma: ','
67 | period: '。'
68 | colon: ':'
69 | brackets_l: '('
70 | brackets_r: ')'
71 |
--------------------------------------------------------------------------------
/languages/zh-TW.yml:
--------------------------------------------------------------------------------
1 | btn:
2 | home: 首頁
3 | blog: 網誌
4 | wiki: Wiki
5 | recent_publish: 近期發布
6 | all_wiki: 所有 Wiki
7 | category: 分類
8 | categories: 分類
9 | tag: 標籤
10 | tags: 標籤
11 | archives: 封存
12 | getting_started: 開始使用
13 | search: 搜索
14 |
15 | meta:
16 | recent_update: 最近更新
17 | toc: 本文目錄
18 | read_next: 接下來閱讀
19 | prev: 回顧上一篇
20 | next: 接下來閱讀
21 | older: 較早文章
22 | newer: 較新文章
23 | references: 參考資料
24 | related_posts: 您可能感興趣的文章
25 | comment_title: 參與討論
26 | back_to_top: 回到頁首
27 | more: '更多%s'
28 | created: 發布於
29 | updated: 更新於
30 | license: 授權條款
31 | share: 分享文章
32 | date_suffix:
33 | just: 剛剛
34 | min: 分鐘前
35 | hour: 小時前
36 | day: 天前
37 | month: 個月前
38 |
39 | footer:
40 | license: '本網誌所有文章除特別聲明外,均採用 %s 授權條款。轉載請註明出處。'
41 | info_not_open_source: '本站由 %s 建立,使用 %s 作為主題。'
42 | info_open_source: '本站由 %s 建立,使用 %s 作為主題。您可以在 %s 找到本站原始碼。'
43 | powered_by_1: '由 '
44 | powered_by_2: ' 強力驅動'
45 | server_render: '當前為服務器渲染模式,部分功能可能已被自動禁用。'
46 |
47 | page:
48 | error:
49 | what: 抱歉,找不到您存取的頁面
50 | why: 可能是網址有誤或已經刪除
51 | action: 返回首頁
52 |
53 | search:
54 | search: 站內搜索
55 | search_in: 在 %s 中搜索
56 | no_results: 沒有找到內容!
57 |
58 | message:
59 | copied: 複製成功
60 | theme_switched:
61 | light: 切換到淺色模式
62 | dark: 切換到深色模式
63 | auto: 切換到跟隨系統配色
64 |
65 | symbol:
66 | comma: ','
67 | period: '。'
68 | colon: ':'
69 | brackets_l: '('
70 | brackets_r: ')'
71 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/about.styl:
--------------------------------------------------------------------------------
1 | .tag-plugin.about
2 | background: var(--block)
3 | border-radius: $border-card
4 | padding: 2rem
5 | position: relative
6 | .nav-back
7 | display: none
8 | position: absolute
9 | line-height: 1
10 | overflow: hidden
11 | left: 0.75rem
12 | top: 0.75rem
13 | align-items: center
14 | @media screen and (max-width: $device-mobile-max)
15 | display: flex
16 | svg
17 | width: 1rem
18 | height: 1rem
19 | .about-header
20 | display flex
21 | justify-content: center
22 | flex-wrap: wrap
23 | margin: 1.5rem 0
24 | img
25 | object-fit: contain
26 | >img
27 | margin: auto 0
28 | >p
29 | font-size: $fs-12
30 | font-weight: 500
31 | color: var(--text-p3)
32 | padding-top: 0.75rem
33 | strong:first-child
34 | font-size: 3rem
35 | font-weight: 700
36 | color: var(--text-p1)
37 | margin-right: 0.75rem
38 | .avatar
39 | display: inline-flex
40 | margin: 0 1rem
41 | .about-body
42 | >p
43 | line-height: 1.5
44 | &:first-child
45 | margin-top: 2.5rem
46 | p+.tag-plugin.navbar .cap
47 | margin-top: 1rem
48 | .about-header+.about-body
49 | margin-top: 2rem
50 |
51 | @media screen and (max-width: $device-mobile)
52 | .tag-plugin.about
53 | padding: 2rem 1rem
54 | .about-header
55 | p
56 | width 100%
57 |
--------------------------------------------------------------------------------
/layout/components/main/post_list/wiki_card.jsx:
--------------------------------------------------------------------------------
1 | module.exports = function WikiCard(props) {
2 | const ProjectLogo = (props) => {
3 | const { proj } = props
4 | if (proj.logo && proj.logo.src) {
5 | return (
6 |
7 |
12 |
13 | )
14 | } else {
15 | return <>>
16 | }
17 | }
18 | const ProjectCategories = (props) => {
19 | const { proj, category_color } = props
20 | if (proj.tags && proj.tags.length > 0) {
21 | const tag = proj.tags[0]
22 | return (
23 |
24 | {tag}
25 |
26 | )
27 | } else {
28 | return <>>
29 | }
30 | }
31 | const ProjectDescription = (props) => {
32 | const { proj } = props
33 | if (proj.description) {
34 | return {proj.description}
35 | } else {
36 | return <>>
37 | }
38 | }
39 | const { proj } = props
40 | return (
41 |
42 |
43 |
44 |
45 |
{proj.title || proj.name}
46 |
47 |
48 |
49 | )
50 | }
51 |
--------------------------------------------------------------------------------
/layout/components/plugins/mathjax/script.jsx:
--------------------------------------------------------------------------------
1 | const MathJaxScripts = (props) => {
2 | const { theme } = props
3 | const { plugins } = theme
4 | const { mathjax } = plugins
5 | const { enabled, js } = mathjax
6 |
7 | if (enabled) {
8 | const loadMathScript = `
9 | function loadMathJax() {
10 | console.log('MathJax enabled')
11 | stellar.loadScript('${js}', {defer:true}).then(() => {
12 | MathJax.Hub.Config({
13 | tex2jax: {
14 | inlineMath: [ ['$','$'], ["\\(","\\)"] ],
15 | processEscapes: true
16 | }
17 | });
18 | MathJax.Hub.Config({
19 | tex2jax: {
20 | skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
21 | }
22 | });
23 | MathJax.Hub.Queue(function() {
24 | var all = MathJax.Hub.getAllJax(), i;
25 | for(i=0; i < all.length; i += 1) {
26 | all[i].SourceElement().parentNode.className += ' has-jax';
27 | }
28 | });
29 | })
30 | }
31 | loadMathJax()
32 | `
33 | // 简单的加载,极致的享受
34 | return (
35 | <>
36 |
37 | >
38 | )
39 | } else {
40 | return <>>
41 | }
42 | }
43 |
44 | module.exports = MathJaxScripts
45 |
--------------------------------------------------------------------------------
/layout/categories.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const NavBarListPost = require('./components/main/navbar/list_post.jsx')
3 | const Categories = (props) => {
4 | const CategoryList = (props) => {
5 | const elements = []
6 | const { site, url_for } = props
7 | site.categories.sort('path').each((category) => {
8 | elements.push(
9 |
18 | )
19 | })
20 | return elements
21 | }
22 |
23 | const { site, scroll_reveal, __ } = props
24 | let { page } = props
25 | page.robots = 'noindex,follow'
26 | if (page.menu_id === undefined) {
27 | page.menu_id = 'post'
28 | }
29 | if (site.categories.length) {
30 | page.title = __('btn.categories')
31 | page.layout = 'categories'
32 | return (
33 |
34 |
35 |
40 |
41 | )
42 | } else {
43 | return <>>
44 | }
45 | }
46 |
47 | module.exports = Categories
48 |
--------------------------------------------------------------------------------
/layout/components/cover/post_cover.jsx:
--------------------------------------------------------------------------------
1 | const PostCover = (props) => {
2 | const { page, theme, scroll_reveal } = props
3 | if (
4 | (page.banner === undefined || page.banner === false) &&
5 | !theme.article.auto_banner
6 | ) {
7 | return <>>
8 | }
9 | let coverUrl = ''
10 | if (page.banner !== undefined) {
11 | if (page.banner.includes('/')) {
12 | coverUrl = page.banner
13 | } else {
14 | coverUrl = 'https://source.unsplash.com/2000x400/?' + page.banner
15 | }
16 | } else {
17 | // 自动以 tags 作为关键词搜索封面
18 | if (page.tags) {
19 | let params = ''
20 | page.tags.reverse().forEach((tag, i) => {
21 | if (i > 0) {
22 | params += ','
23 | }
24 | params += tag.name
25 | })
26 | coverUrl = 'https://source.unsplash.com/2000x400/?' + params
27 | } else {
28 | coverUrl = 'https://source.unsplash.com/random/2000x400'
29 | }
30 | }
31 | let imageElement = ''
32 | if (theme.plugins.lazyload && theme.plugins.lazyload.enabled) {
33 | imageElement =
34 | } else {
35 | imageElement = (
36 |
40 | )
41 | }
42 | return (
43 |
46 | )
47 | }
48 |
49 | module.exports = PostCover
50 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/layout.jsx:
--------------------------------------------------------------------------------
1 | const Comments = (props) => {
2 | const { theme, page, markdown, __ } = props
3 | let loadComment = false
4 | if (theme.comments.service && theme.comments.service.length > 0) {
5 | if (page.comments === undefined || page.comments !== false) {
6 | loadComment = true
7 | }
8 | }
9 | // 合并wiki评论
10 | let proj = theme.wiki.tree[page.wiki]
11 | if (loadComment && page.layout === 'wiki' && page.wiki && proj != null) {
12 | if (proj.comment_title !== undefined && page.comment_title === undefined) {
13 | if (
14 | ['utterances', 'beaudar', 'giscus'].includes(theme.comments.service)
15 | ) {
16 | page.comment_title = proj.comment_title
17 | }
18 | }
19 | }
20 |
21 | if (loadComment) {
22 | const commentTitle =
23 | page.comment_title !== undefined
24 | ? markdown(page.comment_title)
25 | : __('meta.comment_title')
26 | const CommentLayout = require('./' + theme.comments.service + '/layout.jsx')
27 | return (
28 |
37 | )
38 | } else {
39 | return <>>
40 | }
41 | }
42 |
43 | module.exports = Comments
44 |
--------------------------------------------------------------------------------
/scripts/tags/inline-labels.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 修饰文本标签 v2 | https://github.com/chiyuki0325/hexo-theme-stellaris
3 | *
4 | * example:
5 | * {% psw 这是密码 %}
6 | */
7 |
8 | 'use strict'
9 |
10 | hexo.extend.tag.register('u', function (args) {
11 | return `${args.join(' ')} `
12 | })
13 | hexo.extend.tag.register('emp', function (args) {
14 | return `${args.join(' ')} `
15 | })
16 | hexo.extend.tag.register('wavy', function (args) {
17 | return `${args.join(' ')} `
18 | })
19 | hexo.extend.tag.register('del', function (args) {
20 | return `${args.join(' ')}`
21 | })
22 | hexo.extend.tag.register('kbd', function (args) {
23 | return `${args.join(' ')} `
24 | })
25 | hexo.extend.tag.register('psw', function (args) {
26 | return `${args.join(' ')} `
27 | })
28 | hexo.extend.tag.register('sup', function (args) {
29 | args = hexo.args.map(args, ['color'], ['text'])
30 | var el = ''
31 | el +=
32 | ''
36 | el += args.text
37 | el += ' '
38 | return el
39 | })
40 | hexo.extend.tag.register('sub', function (args) {
41 | args = hexo.args.map(args, ['color'], ['text'])
42 | var el = ''
43 | el +=
44 | ''
48 | el += args.text
49 | el += ' '
50 | return el
51 | })
52 |
--------------------------------------------------------------------------------
/source/css/_common/device.styl:
--------------------------------------------------------------------------------
1 | .mobile-only
2 | display: none
3 | @media screen and (max-width: $device-mobile-max)
4 | display: block !important
5 |
6 | .mobile-hidden
7 | @media screen and (max-width: $device-mobile-max)
8 | display: none !important
9 |
10 | .float-panel
11 | position: sticky
12 | right: 0
13 | bottom: 2rem
14 | float: right
15 | z-index: 10
16 | display: flex
17 | border-radius: 2rem
18 | margin-right: 1rem
19 | overflow: hidden
20 | --blur-px: 16px
21 | --blur-bg: alpha(#fff, .4)
22 | trans1: all
23 | if hexo-config('style.darkmode') == 'auto'
24 | @media (prefers-color-scheme: dark)
25 | --blur-bg: alpha(#000, .4)
26 | if hexo-config('style.darkmode') == 'always'
27 | --blur-bg: alpha(#000, .4)
28 |
29 | if hexo-config('style.darkmode') == 'auto-switch'
30 | :root[data-theme="dark"]
31 | .float-panel
32 | --blur-bg: alpha(#000, .4)
33 | :root[data-theme="auto"]
34 | @media (prefers-color-scheme: dark)
35 | .float-panel
36 | --blur-bg: alpha(#000, .4)
37 |
38 |
39 | .sidebar-toggle.mobile
40 | cursor: pointer
41 | color: var(--text-p0)
42 | background: none
43 | padding: 0.5rem
44 | line-height: 0
45 | font-size: 20px
46 | margin: 0
47 |
48 |
49 | .l_body.mobile.sidebar
50 | .float-panel
51 | box-shadow: $boxshadow-float
52 | transform: translateY(-2px)
53 | .sidebar-toggle.mobile
54 | background: var(--card)
55 | color: $color-hover
56 | border-color: var(--block-border)
57 |
--------------------------------------------------------------------------------
/scripts/tags/lib/split.js:
--------------------------------------------------------------------------------
1 | /**
2 | * split.js v2.0 | https://github.com/chiyuki0325/hexo-theme-stellaris
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% split [style:block/card] %}
6 | *
7 | * left body
8 | *
9 | * right body
10 | * {% endgrid %}
11 | */
12 |
13 | 'use strict'
14 |
15 | module.exports = (ctx) =>
16 | function (args, content) {
17 | args = ctx.args.map(args, ['bg'])
18 | var el = ''
19 | el += ''
22 |
23 | var arr = content
24 | .split(//g)
25 | .filter((item) => item.trim().length > 0)
26 | if (arr.length > 0) {
27 | var nodes = []
28 | arr.forEach((item, i) => {
29 | if (i % 2 == 0) {
30 | nodes.push({
31 | header: item,
32 | })
33 | } else if (nodes.length > 0) {
34 | var node = nodes[nodes.length - 1]
35 | if (node.body == undefined) {
36 | node.body = item
37 | } else {
38 | node.body += '\n' + item
39 | }
40 | }
41 | })
42 | nodes.forEach((node, i) => {
43 | el += '
'
44 | el += ctx.render
45 | .renderSync({ text: node.body || '', engine: 'markdown' })
46 | .split('\n')
47 | .join('')
48 | el += '
'
49 | })
50 | }
51 |
52 | el += '
'
53 |
54 | return el
55 | }
56 |
--------------------------------------------------------------------------------
/source/css/_components/sidebar/footer.styl:
--------------------------------------------------------------------------------
1 | .l_left>.footer
2 | margin: 0.5rem var(--gap-l) 1rem
3 |
4 | .social-wrap
5 | display: grid
6 | grid-gap: 0.25rem 0.25rem
7 | grid-template-columns: repeat(auto-fill, 32px)
8 | a.social, .darkmode-switch-container
9 | cursor: pointer
10 | line-height: 0
11 | display: inline-block
12 | padding: 6px
13 | border-radius: 4px
14 | filter: grayscale(100%)
15 | overflow: hidden
16 | background: transparent
17 | border: 0px solid var(--block-border);
18 | trans3: background transform border
19 | &:hover
20 | color: $color-hover
21 | z-index: 1
22 | filter: unset
23 | transform: translateY(-1px)
24 | background: var(--card)
25 | border: 1px solid var(--block-border);
26 | //box-shadow: 2px 4px 8px #ddd, -2px -4px 8px #fff
27 | if hexo-config('style.darkmode') == 'auto'
28 | @media (prefers-color-scheme: dark)
29 | //box-shadow: none
30 | //if hexo-config('style.darkmode') == 'always'
31 | //box-shadow: none
32 |
33 |
34 |
35 | .proj-wrap
36 | display: grid
37 | grid-gap: 0.25rem 0.25rem
38 | a.item
39 | border-radius: 4px
40 | font-size: $fs-13
41 | font-weight: 500
42 | color: var(--text-p2)
43 | background: var(--block)
44 | border: 1px solid var(--block-border)
45 | overflow: hidden
46 | padding: .75em
47 | text-align: center
48 | svg
49 | margin-right: .5em
50 | &:hover
51 | color: $color-hover
52 | background: var(--card)
53 |
--------------------------------------------------------------------------------
/layout/post.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const Breadcrumb = require('./components/main/navbar/breadcrumb.jsx')
3 | const ArticleFooter = require('./components/main/article/article_footer.jsx')
4 | const ReadNext = require('./components/main/article/read_next.jsx')
5 | const RelatedPosts = require('./components/main/article/related_posts.jsx')
6 | const Comments = require('./components/plugins/comments/layout.jsx')
7 | const PostTitle = (props) => {
8 | const { page } = props
9 | const title = page.h1 ?? page.title
10 | if (title && title.length > 0) {
11 | return (
12 |
13 | {title}
14 |
15 | )
16 | } else {
17 | return <>>
18 | }
19 | }
20 | const Post = (props) => {
21 | const { scroll_reveal, partial } = props
22 | let { page } = props
23 | if (page.menu_id === undefined) {
24 | page.menu_id = 'post'
25 | }
26 | if (page.header === undefined) {
27 | page.header = 'auto'
28 | }
29 | return (
30 |
31 |
32 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | )
45 | }
46 |
47 | module.exports = Post
48 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 📑 Stellaris - 强大、优雅、现代的 Hexo 主题
2 |
3 | hexo-theme-stellaris 分叉自 [hexo-theme-stellar](https://github.com/xaoxuu/hexo-theme-stellar),基于[hexo-renderer-jsx](https://github.com/hexojs/hexo-renderer-jsx),支持丰富的标签和动态数据组件。
4 |
5 | 本主题目前已不再活跃维护,并不再同步 Stellar 的新功能特性。如对追新有需求,请使用上游 Stellar 主题,或在 [issue](https://github.com/chiyuki0325/hexo-theme-stellaris/issues/6) 中提交需要使用的新功能。
6 |
7 | ### 安装
8 |
9 | - 环境需求
10 | ```
11 | Hexo: 5.4.0 ~ 6.3.0
12 | hexo-cli: 4.3.0 ~ latest
13 | node.js: 14.17.3 ~ 18.12.0
14 | npm: 6.14.13 ~ 8.19.2
15 | ```
16 |
17 | #### 使用 Git 安装
18 |
19 | - 安装依赖
20 | ```bash
21 | npm install react react-dom hexo-renderer-jsx html-react-parser --save
22 | ```
23 |
24 | - 将主题安装为子模块
25 |
26 | ```bash
27 | git submodule add https://github.com/chiyuki0325/hexo-theme-stellaris.git themes/stellaris
28 | ```
29 |
30 | #### 使用 npm 安装
31 |
32 | ```bash
33 | npm install hexo-theme-stellaris --save
34 | ```
35 |
36 | 安装好后,在 `config.yml` 中添加 `theme: stellaris`。
37 |
38 | ### 更新
39 |
40 | #### 使用 Git
41 |
42 | ```bash
43 | git submodule update --remote --merge
44 | ```
45 |
46 | #### 使用 npm
47 |
48 | ```bash
49 | npm update hexo-theme-stellaris
50 | ```
51 |
52 | ### 文档
53 |
54 | [点此查看](https://blog.chyk.ink/wiki/stellaris/) Stellaris 主题文档。文档正在施工中,欢迎提交贡献。
55 |
56 | 也可以适当参考 [原主题文档](https://xaoxuu.com/wiki/stellar/),或对照配置文件的注释。
57 |
58 | #### Telegram Instant View
59 |
60 | 本主题编写了模板以适配 Telegram Instant View。
61 |
62 | 你可以在此[获取模板](https://blog.chyk.ink/2023/07/15/stellaris-instant-view-template/),并且查阅[官方文档](https://instantview.telegram.org/)以了解如何在你的博客中使用。
63 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/widgets.styl:
--------------------------------------------------------------------------------
1 | .widgets
2 | .loading-wrap
3 | margin: 0.5rem 0
4 |
5 | .widgets
6 | overflow: scroll
7 | flex-grow: 1
8 | scrollbar-width: none
9 | scrollbar(0, 0)
10 | z-index: 1
11 | line-height: 1.2
12 | .widget-wrapper
13 | .widget-header
14 | padding-left: var(--gap-l)
15 | padding-right: var(--gap-l)
16 | display: flex
17 | justify-content: space-between
18 | align-items: center
19 | font-weight: 500
20 | position: sticky
21 | top: -2px
22 | //background: var(--site-bg)
23 | padding-top: 2px
24 | z-index 1
25 | .item
26 | display: block
27 | >span
28 | margin: 0.25rem 0
29 | text-align: left
30 | &:empty
31 | display: none
32 | .cap-action
33 | hover-block 4px 4px
34 | line-height: 0
35 | color: var(--text-meta)
36 | trans2: color background
37 | .icon
38 | fill: var(--text-meta)
39 | &:hover
40 | color: $color-hover
41 | .icon
42 | fill: $color-hover
43 |
44 | .widget-body
45 | margin: 0.5rem var(--gap-l)
46 | color: var(--text-p1)
47 | p
48 | margin-top: .5em
49 | margin-bottom: .5em
50 | line-height: 1.5
51 | .widget-header+.widget-body
52 | margin-top: 0
53 | .widget-wrapper+.widget-wrapper .widget-header
54 | margin-top: 3rem
55 | .widget-wrapper+.widget-wrapper.toc .widget-header
56 | margin-top: 1rem
57 |
58 | .widget-wrapper
59 | display: block
60 | margin: 2rem 0
--------------------------------------------------------------------------------
/source/css/_components/partial/navbar.styl:
--------------------------------------------------------------------------------
1 | .nav-wrap
2 | position: sticky
3 | position: -webkit-sticky
4 | margin-top: -0.5rem
5 | top: -2px
6 | background: var(--site-bg)
7 | padding: 0 1rem
8 | z-index: 8
9 | margin-bottom: 1px
10 | //&:after
11 | //content: ''
12 | //width: 'calc(100% - 2 * %s)' % 1rem
13 | //height: 2px
14 | //border-radius: 2px
15 | //position: absolute
16 | //bottom: 0
17 | //left: 1rem
18 | //background: var(--block-hover)
19 |
20 | nav.cap
21 | display: flex
22 | overflow: auto
23 | scrollbar(0, 0)
24 | >p
25 | margin: 0
26 | a
27 | padding: .25rem 1rem
28 | margin: 10px 0 8px 0
29 | line-height: 2
30 | color: var(--text-p3)
31 | border-radius: 4px
32 | font-weight: 500
33 | white-space: nowrap
34 | position: relative
35 | z-index: 1
36 | &:after
37 | height: 2px
38 | position: absolute
39 | bottom: -1px
40 | left: 1rem
41 | right: 1rem
42 | background: var(--theme-highlight)
43 | border-radius: 2px
44 | pointer-events: none
45 | &:hover
46 | background: var(--block-hover)
47 | &.active, &:hover
48 | color: var(--text-p1)
49 | &.active
50 | background: var(--card)
51 | //box-shadow: $boxshadow-button
52 | &.active:after
53 | content: ''
54 | a+a
55 | margin-left: 4px
56 |
57 | @media screen and (max-width: $device-mobile-max)
58 | .nav-wrap
59 | margin-top: 0
60 | padding-left: 0
61 | padding-right: 0
62 | nav
63 | a:first-child
64 | margin-left: 1rem
65 | a:last-child
66 | margin-right: 1rem
67 |
--------------------------------------------------------------------------------
/scripts/tags/lib/ghcard.js:
--------------------------------------------------------------------------------
1 | /**
2 | * friends.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% ghcard user/repo [theme:xxx] %} or {% ghcard user %}
6 | *
7 | * example:
8 | * {% ghcard xaoxuu %}
9 | * {% ghcard xaoxuu/hexo-theme-stellar %}
10 | *
11 | * API: https://github.com/anuraghazra/github-readme-stats
12 | */
13 |
14 | 'use strict'
15 |
16 | module.exports = (ctx) =>
17 | function (args) {
18 | var params = [
19 | 'show_owner',
20 | 'theme',
21 | 'title_color',
22 | 'text_color',
23 | 'icon_color',
24 | 'bg_color',
25 | 'hide_border',
26 | 'cache_seconds',
27 | 'locale',
28 | ]
29 | args = ctx.args.map(args, params, ['repo'])
30 | const path = args.repo
31 | var el = ''
32 | el += ''
56 | return el
57 | }
58 |
--------------------------------------------------------------------------------
/scripts/tags/lib/navbar.js:
--------------------------------------------------------------------------------
1 | /**
2 | * navbar.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% navbar [markdown link] ... %}
6 | *
7 | * example:
8 | * {% navbar active:1 [Home](/) [About](/about/) [Comments](#comments) %}
9 | */
10 |
11 | 'use strict'
12 |
13 | module.exports = (ctx) =>
14 | function (args) {
15 | if (args.length == 0) {
16 | return
17 | }
18 | args = ctx.args.map(args, ['active'], ['links'])
19 | if (args.links) {
20 | args.links = args.links.split(' ')
21 | }
22 | var el = ''
57 | return el
58 | }
59 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/folders.styl:
--------------------------------------------------------------------------------
1 | .tag-plugin.folders
2 | display: block
3 | margin: 1rem 0
4 | border-radius: $border-block
5 | font-size: $fs-14
6 | border: 1px solid var(--theme-border)
7 | overflow: hidden
8 | .folder
9 | display: block
10 | font-size: $fs-14
11 | background: var(--block)
12 | &:last-child summary
13 | border-bottom: none
14 | summary
15 | display: block
16 | cursor: pointer
17 | color: var(--text-p2)
18 | font-weight: 500
19 | position: relative
20 | line-height: 1.2
21 | outline: none
22 | padding: 0.75rem 1rem
23 | border-bottom: 1px solid var(--theme-border)
24 | &:last-child
25 | border-bottom: none
26 | >
27 | p,h1,h2,h3,h4,h5,h6
28 | display: inline
29 | border-bottom: none !important
30 | &:hover
31 | color: var(--text-p0)
32 | background: var(--block-hover)
33 | &:after
34 | position: absolute
35 | content: '+'
36 | text-align: center
37 | top: 50%
38 | transform: translateY(-50%)
39 | right: 1rem
40 |
41 |
42 | .tag-plugin.folders details[open]
43 | background: none
44 | &:last-child
45 | summary
46 | border-bottom: 1px solid var(--theme-border)
47 | .body
48 | border-bottom: none
49 | >summary
50 | color: var(--text-p1)
51 | background: var(--block-hover)
52 | &:hover:after
53 | content: '-'
54 |
55 | >div.body
56 | padding: 1rem
57 | border-bottom: 1px solid var(--theme-border)
58 | font-size: $fs-15
59 | >:first-child
60 | margin-top: 0
61 | >:last-child
62 | margin-bottom: 0
63 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/giscus/layout.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const GiscusLayout = (props) => {
3 | const { page, theme } = props
4 | const cmt = 'giscus'
5 | // 合并配置参数
6 | var config = Object.assign({}, theme.comments[cmt])
7 | if (page.layout === 'wiki' && page.wiki) {
8 | let proj = theme.wiki.projects[page.wiki]
9 | if (proj[cmt] !== undefined) {
10 | Object.assign(config, proj[cmt])
11 | }
12 | }
13 | Object.assign(config, page[cmt])
14 | // 合并评论数据
15 | if (config['issue-number'] !== null) {
16 | config['issue-term'] = null
17 | } else {
18 | if (page.comment_id !== undefined) {
19 | config['issue-term'] = page.comment_id
20 | } else if (page.layout === 'wiki' && page.wiki) {
21 | let proj = theme.wiki.projects[page.wiki]
22 | if (proj.comment_id !== undefined) {
23 | config['issue-term'] = proj.comment_id
24 | }
25 | }
26 | }
27 | // 布局
28 | return (
29 |
30 |
42 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | module.exports = GiscusLayout
53 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/beaudar/layout.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const BeaudarLayout = (props) => {
3 | const { page, theme } = props
4 | const cmt = 'beaudar'
5 | // 合并配置参数
6 | var config = Object.assign({}, theme.comments[cmt])
7 | if (page.layout === 'wiki' && page.wiki) {
8 | let proj = theme.wiki.projects[page.wiki]
9 | if (proj[cmt] !== undefined) {
10 | Object.assign(config, proj[cmt])
11 | }
12 | }
13 | Object.assign(config, page[cmt])
14 | // 合并评论数据
15 | if (config['issue-number'] !== null) {
16 | config['issue-term'] = null
17 | } else {
18 | if (page.comment_id !== undefined) {
19 | config['issue-term'] = page.comment_id
20 | } else if (page.layout === 'wiki' && page.wiki) {
21 | let proj = theme.wiki.projects[page.wiki]
22 | if (proj.comment_id !== undefined) {
23 | config['issue-term'] = proj.comment_id
24 | }
25 | }
26 | }
27 | // 布局
28 | return (
29 |
30 |
42 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | module.exports = BeaudarLayout
53 |
--------------------------------------------------------------------------------
/layout/components/plugins/comments/utterances/layout.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const UtterancesLayout = (props) => {
3 | const { page, theme } = props
4 | const cmt = 'utterances'
5 | // 合并配置参数
6 | var config = Object.assign({}, theme.comments[cmt])
7 | if (page.layout === 'wiki' && page.wiki) {
8 | let proj = theme.wiki.projects[page.wiki]
9 | if (proj[cmt] !== undefined) {
10 | Object.assign(config, proj[cmt])
11 | }
12 | }
13 | Object.assign(config, page[cmt])
14 | // 合并评论数据
15 | if (config['issue-number'] !== null) {
16 | config['issue-term'] = null
17 | } else {
18 | if (page.comment_id !== undefined) {
19 | config['issue-term'] = page.comment_id
20 | } else if (page.layout === 'wiki' && page.wiki) {
21 | let proj = theme.wiki.projects[page.wiki]
22 | if (proj.comment_id !== undefined) {
23 | config['issue-term'] = proj.comment_id
24 | }
25 | }
26 | }
27 | // 布局
28 | return (
29 |
30 |
42 |
46 |
47 |
48 |
49 | )
50 | }
51 |
52 | module.exports = UtterancesLayout
53 |
--------------------------------------------------------------------------------
/scripts/tags/lib/folders.js:
--------------------------------------------------------------------------------
1 | /**
2 | * folders.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% folders [color:white] %}
6 | *
7 | * body 1
8 | *
9 | * body 2
10 | * {% endtable %}
11 | */
12 |
13 | 'use strict'
14 |
15 | module.exports = (ctx) =>
16 | function (args, content) {
17 | args = ctx.args.map(args, ['color'])
18 | var el = ''
19 | el += ''
22 |
23 | var arr = content
24 | .split(//g)
25 | .filter((item) => item.trim().length > 0)
26 | if (arr.length > 0) {
27 | var nodes = []
28 | arr.forEach((item, i) => {
29 | if (i % 2 == 0) {
30 | nodes.push({
31 | header: item,
32 | })
33 | } else if (nodes.length > 0) {
34 | var node = nodes[nodes.length - 1]
35 | if (node.body == undefined) {
36 | node.body = item
37 | } else {
38 | node.body += '\n' + item
39 | }
40 | }
41 | })
42 | nodes.forEach((node, i) => {
43 | el += '
'
44 | // summary
45 | el += '' + (node.header || '') + ' '
46 | // content
47 | el += ''
48 | el += ctx.render
49 | .renderSync({ text: node.body || '', engine: 'markdown' })
50 | .split('\n')
51 | .join('')
52 | el += '
'
53 | })
54 | }
55 |
56 | el += '
'
57 |
58 | return el
59 | }
60 |
--------------------------------------------------------------------------------
/source/css/_components/partial/cover.styl:
--------------------------------------------------------------------------------
1 | .l_cover
2 | height: 100vh
3 | text-align: center
4 | display: flex
5 | flex-direction: column
6 | justify-content: center
7 | align-items: center
8 | position: relative
9 | &.post
10 | height: inherit
11 |
12 |
13 | .l_cover .cover-wrap
14 | margin-bottom: 0
15 | display: flex
16 | flex-direction: column
17 | justify-content: center
18 | align-items: center
19 | .cover-title
20 | font-weight: 700
21 | font-size: 1.5rem
22 | margin: 1rem 0
23 | line-height: 1.2
24 | .description
25 | margin: 1rem 0
26 | .start-wrap
27 | margin: 2rem 0
28 | flex-shrink: 0
29 | a.start
30 | display: inline-block
31 |
32 | .l_cover.post
33 | .cover
34 | z-index: -1
35 | width: 100%
36 | height: 30vh
37 | max-width: $device-2k
38 | min-height: 150px
39 | max-height: 400px
40 | @media screen and (max-width: $device-tablet)
41 | height: 25vh
42 | @media screen and (max-width: $device-mobile)
43 | height: 20vh
44 | @media screen and (min-width: $device-2k)
45 | margin-top: 4rem
46 | .img
47 | border-radius: 2rem
48 |
49 |
50 | .l_cover.post .cover-wrap
51 | z-index: 1
52 | .article-title
53 | text-align: center
54 | padding: 1rem
55 | margin: 1em 0 0 0
56 | @media screen and (min-width: $device-2k)
57 | font-size: 3rem
58 |
59 |
60 | .l_cover.wiki .cover-wrap
61 | max-width: $device-mobile
62 | .preview
63 | margin-bottom: 2rem
64 | img
65 | object-fit: contain
66 | max-height: 35vh
67 | max-width: 100%
68 | @media screen and (max-width: $device-mobile)
69 | max-width: 60%
70 | .cover-title
71 | &:first-child
72 | font-size: 3rem
73 |
--------------------------------------------------------------------------------
/layout/page.jsx:
--------------------------------------------------------------------------------
1 | const Breadcrumb = require('./components/main/navbar/breadcrumb.jsx')
2 | const NavBarListPost = require('./components/main/navbar/list_post.jsx')
3 | const ArticleFooter = require('./components/main/article/article_footer.jsx')
4 | const Comments = require('./components/plugins/comments/layout.jsx')
5 | const PageTitle = (props) => {
6 | const { page } = props
7 | const title = page.h1 ?? page.title
8 | if (title && title.length > 0) {
9 | return (
10 |
11 | {title}
12 |
13 | )
14 | } else {
15 | return <>>
16 | }
17 | }
18 | const Page = (props) => {
19 | const { scroll_reveal, partial } = props
20 | let { page } = props
21 | if (page.menu_id === undefined) {
22 | page.menu_id = 'post'
23 | }
24 | if (page.header === undefined) {
25 | page.header = 'auto'
26 | }
27 | const elements = []
28 | if (page.post_list) {
29 | elements.push( )
30 | }
31 | if (page.h1 || page.title || (page.content && page.content.length > 0)) {
32 | elements.push()
33 | }
34 | elements.push(
35 |
39 | {(page.h1 ?? page.title) ? : <>>}
40 | {page.content ? (
41 |
42 | ) : (
43 | <>>
44 | )}
45 |
46 |
47 | )
48 | elements.push( )
49 | return elements
50 | }
51 |
52 | module.exports = Page
53 |
--------------------------------------------------------------------------------
/scripts/tags/lib/about.js:
--------------------------------------------------------------------------------
1 | /**
2 | * about.js v2 | https://github.com/chiyuki0325/hexo-theme-stellaris/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% about [avatar:xxx] [height:80px] %}
6 | * title / body
7 | * {% endabout %}
8 | */
9 |
10 | 'use strict'
11 |
12 | module.exports = (ctx) =>
13 | function (args, content) {
14 | const url_for = require('hexo-util').url_for.bind(ctx)
15 |
16 | args = ctx.args.map(args, ['avatar', 'height', 'border', 'back'])
17 | var rows = ctx.render
18 | .renderSync({ text: content, engine: 'markdown' })
19 | .split('\n')
20 | var el = ''
21 | // wrapper
22 | el += ''
23 | if (args.back) {
24 | el += '
'
25 | el +=
26 | ' '
27 | el += ' '
28 | }
29 | // avatar
30 | var avatar_url = args.avatar
31 | if (avatar_url) {
32 | el += ''
44 | }
45 |
46 | // content
47 | el += '
'
48 | el += rows.join('')
49 | el += '
'
50 |
51 | el += '
'
52 | return el
53 | }
54 |
--------------------------------------------------------------------------------
/scripts/tags/lib/quot.js:
--------------------------------------------------------------------------------
1 | /**
2 | * quot.js v1.2 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * quot:
6 | * {% quot [el:h2] [icon:default] [prefix:icon] text [suffix:icon] %}
7 | *
8 | */
9 |
10 | 'use strict'
11 |
12 | module.exports = (ctx) =>
13 | function (args) {
14 | var el = ''
15 | args = ctx.args.map(args, ['el', 'icon', 'prefix', 'suffix'], ['text'])
16 | if (!args.el) {
17 | args.el = 'p'
18 | }
19 |
20 | var type = ''
21 | if (args.icon || args.prefix || args.suffix) {
22 | type = ' type="icon"'
23 | } else {
24 | type = ' type="text"'
25 | }
26 | function content() {
27 | const cfg = ctx.theme.config.tag_plugins.quot[args.icon]
28 | var el = ''
29 | var prefix = args.prefix || cfg?.prefix
30 | var suffix = args.suffix || cfg?.suffix
31 | if (prefix) {
32 | el += ctx.utils.icon(prefix, 'class="icon prefix"')
33 | } else {
34 | el += ` `
35 | }
36 | el += `${args.text} `
37 | if (suffix) {
38 | el += ctx.utils.icon(suffix, 'class="icon prefix"')
39 | } else {
40 | el += ` `
41 | }
42 | return el
43 | }
44 | if (args.el.includes('h')) {
45 | el += ''
46 | el +=
47 | '<' + args.el + ' class="content" id="' + args.text + '"' + type + '>'
48 | el += content()
49 | el += '' + args.el + '>'
50 | el += '
'
51 | } else {
52 | el += ''
53 | el += '<' + args.el + ' class="content"' + type + '>'
54 | el += content()
55 | el += '' + args.el + '>'
56 | el += '
'
57 | }
58 | return el
59 | }
60 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/friends.styl:
--------------------------------------------------------------------------------
1 | .users-wrap
2 | overflow: hidden
3 | .group-header
4 | margin: 0 0 1rem
5 | p
6 | margin: 0
7 | font-size: $fs-14
8 | &:first-child
9 | font-size: 1.25rem
10 | font-weight: 500
11 | .group-body
12 | width: 100%
13 | display: flex
14 | flex-wrap: wrap
15 | align-items: stretch
16 | &+.group-header
17 | margin-top: 2rem
18 | .stellar-friends-api
19 | display: block
20 |
21 |
22 | .users-wrap .user-card
23 | flex-shrink: 1
24 | display: flex
25 | align-items: stretch
26 | width: 12.5%
27 | @media screen and (max-width: 980px)
28 | width: 14.28%
29 | @media screen and (max-width: 900px)
30 | width: 16.66%
31 | @media screen and (max-width: 820px)
32 | width: 20%
33 | @media screen and (max-width: $device-mobile-max)
34 | width: 16.66%
35 | @media screen and (max-width: $device-mobile)
36 | width: 25%
37 | .card-link
38 | margin: 0
39 | width: 100%
40 | color: var(--text-p1)
41 | font-size: 10px
42 | font-weight: 500
43 | display: flex
44 | justify-content: flex-start
45 | flex-direction: column
46 | align-items: center
47 | text-align: center
48 | line-height: 1.2
49 | border-radius: 4px
50 | overflow: hidden
51 | position: relative
52 | padding: 1rem 0.5rem
53 | img
54 | object-fit: cover
55 | display: block
56 | width: 48px
57 | height: 48px
58 | background: var(--card)
59 | border-radius: 64px
60 | margin: 0 0 0.5rem
61 |
62 | // transform
63 | .users-wrap .user-card .card-link
64 | >img
65 | trans2 transform box-shadow
66 | &:hover
67 | background: var(--block-hover)
68 | img
69 | transform: scale(1.2) rotate(8deg)
70 | box-shadow: $boxshadow-card-float
71 |
--------------------------------------------------------------------------------
/layout/components/sidebar/header.jsx:
--------------------------------------------------------------------------------
1 | const SidebarMenu = require('./menu.jsx')
2 | const HeaderTitle = require('./title.jsx')
3 | const SidebarHeader = (props) => {
4 | const HeaderAvatar = (props) => {
5 | const { url_for, theme, md_text, md_link } = props
6 | if (md_text(theme.sidebar.logo.avatar)) {
7 | return (
8 |
12 | {theme.style.animated_avatar.animate && (
13 |
20 | )}
21 |
27 |
28 | )
29 | }
30 | }
31 | const HeaderMain = (props) => {
32 | const { theme, md_text, md_link, config } = props
33 | let main = md_text(theme.sidebar.logo.title)
34 | if (main) {
35 | let url = md_link(theme.sidebar.logo.title)
36 | let sub = config.subtitle
37 | return
38 | }
39 | }
40 |
41 | const { page, where } = props
42 | if (page.layout === 'wiki' && page.menu_id === 'wiki') {
43 | return <>>
44 | } else {
45 | return (
46 |
47 |
48 |
49 |
50 |
51 | {where !== 'main' && }
52 |
53 | )
54 | }
55 | }
56 |
57 | module.exports = SidebarHeader
58 |
--------------------------------------------------------------------------------
/languages/en.yml:
--------------------------------------------------------------------------------
1 | btn:
2 | home: Home
3 | blog: Blog
4 | wiki: Wiki
5 | recent_publish: Recent
6 | all_wiki: All Products
7 | category: Category
8 | categories: Categories
9 | tag: Tag
10 | tags: Tags
11 | archives: Archives
12 | getting_started: Getting Started
13 | search: Search
14 |
15 | meta:
16 | recent_update: Recent Update
17 | toc: TOC
18 | read_next: READ NEXT
19 | prev: Prev
20 | next: Next
21 | older: Older
22 | newer: Newer
23 | references: References
24 | related_posts: Related Posts
25 | comment_title: Join the discussion
26 | back_to_top: Back to top
27 | more: 'More %s'
28 | created: 'Posted on'
29 | updated: 'Updated on'
30 | license: License
31 | share: Share
32 | date_suffix:
33 | just: Just
34 | min: minutes ago
35 | hour: hours ago
36 | day: days ago
37 | month: months ago
38 |
39 | footer:
40 | license: 'All articles in this blog are licensed under %s unless stating additionally.'
41 | info_not_open_source: 'This site was deployed by %s using %s.'
42 | info_open_source: 'This site was deployed by %s using %s. You can find the source code in %s.' #已经改为相关信息
43 | powered_by_1: 'Powered by '
44 | powered_by_2: ' '
45 | server_render: 'Server-side rendering mode is currently enabled, and some functions may have been automatically disabled.'
46 | page:
47 | error:
48 | what: Page Not Found
49 | why: The address may be entered incorrectly or the address has been deleted.
50 | action: Back to Home
51 |
52 | search:
53 | search: Search
54 | search_in: Search in %s
55 | no_results: No Results!
56 |
57 | message:
58 | copied: Copied!
59 | theme_switched:
60 | light: Switched to Light Mode
61 | dark: Switched to Dark Mode
62 | auto: Switched to Auto Mode
63 |
64 | symbol:
65 | comma: ', '
66 | period: '. '
67 | colon: ': '
68 | brackets_l: '('
69 | brackets_r: ')'
70 |
--------------------------------------------------------------------------------
/layout/components/widgets/related.jsx:
--------------------------------------------------------------------------------
1 | const Related = (props) => {
2 | const { theme, page, __, url_for } = props
3 | if (page.layout !== 'wiki') {
4 | return <>>
5 | }
6 |
7 | let related = []
8 | const { shelf, tree } = theme.wiki
9 | let proj = tree[page.wiki]
10 | if (proj?.related?.length > 0) {
11 | proj.related
12 | .filter((pid) => shelf.includes(pid))
13 | .forEach((pid, i) => {
14 | let p = tree[pid]
15 | if (p && p.title !== proj?.title && p.homepage) {
16 | related.push(p)
17 | }
18 | })
19 | }
20 |
21 | if (related.length > 0) {
22 | return (
23 |
24 |
25 | {(() => {
26 | let title = __('btn.wiki')
27 | if (proj.tags && proj.tags[0]) {
28 | title = proj.tags[0]
29 | }
30 | return {__('meta.more', title)}
31 | })()}
32 |
33 |
52 |
53 | )
54 | } else {
55 | return <>>
56 | }
57 | }
58 |
59 | module.exports = Related
60 |
--------------------------------------------------------------------------------
/layout/wiki.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const Index = require('./index.jsx')
3 | const Breadcrumb = require('./components/main/navbar/breadcrumb.jsx')
4 | const ArticleFooter = require('./components/main/article/article_footer.jsx')
5 | const ReadNext = require('./components/main/article/read_next.jsx')
6 | const Comments = require('./components/plugins/comments/layout.jsx')
7 | const WikiTitle = (props) => {
8 | const { page } = props
9 | const title = page.h1 ?? page.title
10 | if (title && title.length > 0) {
11 | return (
12 |
13 | {title}
14 |
15 | )
16 | } else {
17 | return <>>
18 | }
19 | }
20 | const Wiki = (props) => {
21 | const { scroll_reveal, partial, __ } = props
22 | let { page } = props
23 | if (page.menu_id === undefined) {
24 | page.menu_id = 'wiki'
25 | }
26 | if (page.layout === undefined) {
27 | page.layout = 'wiki_index'
28 | }
29 | if (page.title === undefined) {
30 | if (page.tag_name) {
31 | page.title = page.tag_name
32 | } else {
33 | page.title = __('btn.wiki')
34 | }
35 | }
36 | if (page.layout === 'wiki_index') {
37 | return
38 | } else {
39 | if (page.header === undefined) {
40 | page.header = 'auto'
41 | }
42 | return (
43 |
44 |
45 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 | )
57 | }
58 | }
59 |
60 | module.exports = Wiki
61 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/toc_common.styl:
--------------------------------------------------------------------------------
1 | .widget-wrapper.toc .widget-header
2 | margin-top: 1rem
3 |
4 | .widget-wrapper.toc.single .widget-header
5 | font-weight: 500
6 | font-size: $fs-12
7 | background: var(--site-bg)
8 | >span
9 | margin: 0.5rem 0
10 |
11 | .widget-wrapper.toc .widget-body
12 | margin-top: 0
13 | ul ul, ul ol
14 | padding-left: 0
15 | ol ul, ol ol
16 | padding-left: 0
17 |
18 | .doc-tree
19 | margin: 4px 0
20 | .toc
21 | padding: 0
22 | margin: 0
23 | padding-left: 0.25rem
24 | .toc-item .toc-link
25 | padding: 0.5rem
26 | font-weight: 500
27 | font-size: $fs-13
28 | color: var(--text-p2)
29 | .toc-child .toc-item .toc-link
30 | padding: 0.25rem 0.5rem 0.25rem 1.3rem
31 | font-weight: 400
32 | color: var(--text-p2)
33 | .toc-child .toc-child .toc-item .toc-link
34 | padding-left: 2.1rem
35 | font-size: $fs-12
36 | color: var(--text-p3)
37 | .toc-child .toc-child .toc-child .toc-item .toc-link
38 | padding-left: 2.9rem
39 |
40 |
41 | .widget-wrapper.toc .toc-item
42 | color: var(--text-p2)
43 | font-size: $fs-12
44 | padding: 0
45 | list-style: none
46 | &.active
47 | color: $color-theme
48 | border-left-color: @color
49 | .toc-child .toc-item
50 | padding: 0
51 | .widget-wrapper.toc a.toc-link
52 | color: inherit
53 | display: block
54 | line-height: 1.2
55 | border-radius: 4px
56 | position: relative
57 | &:before
58 | content: ''
59 | position: absolute
60 | left: -6px
61 | top: 'calc(50% - %s)' % 6px
62 | bottom: 'calc(50% - %s)' % 6px
63 | width: 2px
64 | border-radius: 2px
65 | background: $color-theme
66 | visibility: hidden
67 | &:hover
68 | background: var(--block-hover)
69 | &.active
70 | color: var(--theme-highlight) !important
71 | &:before
72 | visibility: visible
73 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/folding.styl:
--------------------------------------------------------------------------------
1 | details.folding
2 | display: block
3 | padding: 1rem
4 | margin: 1rem 0
5 | border-radius: $border-block
6 | box-shadow: $boxshadow-card
7 | font-size: $fs-14
8 | background: var(--theme-bg)
9 | border: 1px solid var(--theme-border)
10 | summary
11 | cursor: pointer
12 | padding: .75rem 1rem
13 | margin: 0 - 1rem
14 | border-radius: $border-block
15 | color: var(--text-p2)
16 | font-weight: 500
17 | position: relative
18 | line-height: normal
19 | outline: none
20 | >
21 | p,h1,h2,h3,h4,h5,h6
22 | display: inline
23 | border-bottom: none !important
24 | &:hover
25 | color: var(--text-p0)
26 | &:after
27 | position: absolute
28 | content: '+'
29 | text-align: center
30 | top: 50%
31 | transform: translateY(-50%)
32 | right: 1rem
33 |
34 |
35 | details.folding[open]
36 | >summary
37 | border-bottom: 1px solid var(--theme-border)
38 | border-bottom-left-radius: 0
39 | border-bottom-right-radius: 0
40 | color: var(--text-p1)
41 | margin-bottom: 0
42 | &:hover
43 | &:after
44 | content: '-'
45 | >div.body
46 | display: flex;
47 | flex-direction: column;
48 | padding: 1rem
49 | margin: 0 - 1rem
50 | margin-top: 0
51 | background: var(--card)
52 | border-bottom-left-radius: 'calc(%s - 1px)' % $border-block
53 | border-bottom-right-radius: 'calc(%s - 1px)' % $border-block
54 | font-size: $fs-15
55 | >:first-child
56 | margin-top: 0
57 | >:last-child
58 | margin-bottom: 0
59 |
60 | details.folding[child=codeblock]>div.body
61 | padding: 0
62 | background: transparent
63 | overflow: hidden
64 | .highlight
65 | border: none
66 | border-radius: 0
67 | background: transparent
68 | margin: 0
69 | .code:before
70 | content: none
71 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/quot.styl:
--------------------------------------------------------------------------------
1 | .md-text .tag-plugin.quot
2 | text-align: center
3 | align-items: center
4 | display: flex
5 | flex-direction: column
6 |
7 | .content
8 | display: flex
9 | align-items: center
10 | border-bottom: none
11 | font-weight: 700
12 | padding: 16px 24px
13 | a.headerlink:before
14 | content: ''
15 | @media screen and (max-width: $device-mobile)
16 | padding: 12px 20px
17 | line-height: 1.2
18 |
19 | .md-text .tag-plugin.quot .content
20 | max-width 500px
21 | position: relative
22 | // override
23 | .md-text.content .tag-plugin.quot
24 | h1
25 | font-size: $fs-h1
26 | font-weight: 900
27 | padding: 20px 32px 12px 32px
28 | @media screen and (max-width: $device-mobile)
29 | padding: 20px 24px 12px
30 | h2,h3,h4,h5,h6
31 | font-size: $fs-h3
32 | margin-top: 1em
33 | margin-bottom: 0
34 | p
35 | font-size: $fs-h4
36 | color: var(--text-p0)
37 |
38 | // type=text
39 | .md-text .tag-plugin.quot
40 | .content[type=text]
41 | &:before,&:after
42 | content: ""
43 | position: absolute
44 | width: 8px
45 | height: 14px
46 | &:before
47 | top: 8px
48 | left: 0
49 | border-top: 6px solid $color-accent
50 | border-left: 6px solid $color-accent
51 | &:after
52 | right: 0
53 | bottom: 8px
54 | border-right: 6px solid $color-accent
55 | border-bottom: 6px solid $color-accent
56 | h1.content[type=text]
57 | &:before,&:after
58 | width: 12px
59 | height: 20px
60 | border-width: 8px
61 |
62 | // type=icon
63 | .md-text .tag-plugin.quot .content[type=icon]
64 | .icon
65 | height: 1.5em
66 | display: inline-block
67 | color: $color-accent
68 | border-radius: 0
69 | &.prefix
70 | margin-left: -0.5rem
71 | margin-right: .5rem
72 | &.suffix
73 | margin-left: .5rem
74 |
--------------------------------------------------------------------------------
/scripts/tags/lib/hashtag.js:
--------------------------------------------------------------------------------
1 | /**
2 | * hashtag.js v1.0 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% hashtag text href [color:color] %}
6 | *
7 | */
8 |
9 | 'use strict'
10 |
11 | const tag_colors = [
12 | 'red',
13 | 'orange',
14 | 'yellow',
15 | 'green',
16 | 'cyan',
17 | 'blue',
18 | 'purple',
19 | ]
20 | var tag_index = 0
21 |
22 | module.exports = (ctx) =>
23 | function (args) {
24 | args = ctx.args.map(args, ['color'], ['text', 'href'])
25 | if (args.color == null) {
26 | const default_color = ctx.theme.config.tag_plugins.hashtag?.default_color
27 | if (default_color) {
28 | args.color = default_color
29 | } else {
30 | args.color = tag_colors[tag_index]
31 | tag_index += 1
32 | if (tag_index >= tag_colors.length) {
33 | tag_index = 0
34 | }
35 | }
36 | }
37 | var el = ''
38 | el += ''
41 | el +=
42 | ' '
43 | el += '' + args.text + ' '
44 | el += ' '
45 | return el
46 | }
47 |
--------------------------------------------------------------------------------
/layout/components/menu_button.jsx:
--------------------------------------------------------------------------------
1 | const MenuButton = () => {
2 | return (
3 |
4 |
34 |
35 | )
36 | }
37 |
38 | module.exports = MenuButton
39 |
--------------------------------------------------------------------------------
/scripts/tags/lib/copy.js:
--------------------------------------------------------------------------------
1 | /**
2 | * copy.js v2 | https://github.com/chiyuki0325/hexo-theme-stellaris/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% copy xxx %}
6 | * {% copy git xaoxuu/hexo-theme-stellar %}
7 | *
8 | */
9 |
10 | 'use strict'
11 |
12 | let copy_index = 0
13 |
14 | module.exports = (ctx) =>
15 | function (args) {
16 | args = ctx.args.map(args, ['width', 'git'], ['text'])
17 | if (args == undefined || args.text == undefined) {
18 | return ''
19 | }
20 | var text = args.text
21 | if (args.git) {
22 | if (text.substr(0, 1) == '/') {
23 | text = text.substring(1)
24 | }
25 | if (args.git == 'ssh') {
26 | text = 'git@github.com:' + text + '.git'
27 | } else if (args.git == 'gh') {
28 | text = 'gh repo clone ' + text
29 | } else {
30 | text = 'https://github.com/' + text + '.git'
31 | }
32 | }
33 |
34 | const copy_id = 'copy_' + ++copy_index
35 |
36 | var el = ''
37 | el += ''
42 | el += '
'
44 | el +=
45 | '
'
48 | el +=
49 | ' '
50 | el += ' '
51 |
52 | el += '
'
53 | return el
54 | }
55 |
--------------------------------------------------------------------------------
/scripts/tags/lib/friends.js:
--------------------------------------------------------------------------------
1 | /**
2 | * friends.js v2 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% friends [group] [repo:owner/repo] [api:http] %}
6 | */
7 |
8 | 'use strict'
9 |
10 | module.exports = (ctx) =>
11 | function (args) {
12 | args = ctx.args.map(args, ['repo', 'api'], ['group'])
13 | var links = ctx.locals.get('data').links
14 | if (links == undefined) {
15 | links = {}
16 | }
17 | var api
18 | if (args.api) {
19 | api = args.api
20 | } else if (args.repo) {
21 | api = 'https://api.vlts.cc/output_data/v2/' + args.repo
22 | }
23 |
24 | var el = ''
25 | if (api) {
26 | el += '
'
29 | el += '
'
30 | el += '
'
31 | } else if (args.group) {
32 | function cell(item) {
33 | if (item.url && item.title) {
34 | var cell = '
'
47 | return cell
48 | } else {
49 | return ''
50 | }
51 | }
52 | el += '
'
53 | const items = links[args.group] || []
54 | items.forEach((item, i) => {
55 | el += cell(item)
56 | })
57 | el += '
'
58 | }
59 |
60 | el += '
'
61 | return el
62 | }
63 |
--------------------------------------------------------------------------------
/scripts/filters/img_lazyload.js:
--------------------------------------------------------------------------------
1 | /**
2 | * img_lazyload.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | *
4 | */
5 |
6 | 'use strict'
7 |
8 | const fs = require('hexo-fs')
9 |
10 | function lazyProcess(htmlContent) {
11 | const cfg = this.theme.config.plugins.lazyload
12 | if (cfg == undefined || cfg.enabled != true) {
13 | return htmlContent
14 | }
15 | return htmlContent.replace(
16 | / /gi,
17 | function (imgTag, src_before, src_value, src_after) {
18 | // might be duplicate
19 | if (/data-srcset/gi.test(imgTag)) {
20 | return imgTag
21 | }
22 | if (/src="data:image(.*?)/gi.test(imgTag)) {
23 | return imgTag
24 | }
25 | if (imgTag.includes(' no-lazy ')) {
26 | return imgTag
27 | }
28 | var newImgTag = imgTag
29 | if (newImgTag.includes(' class="') == false) {
30 | newImgTag = newImgTag.slice(0, 4) + ' class=""' + newImgTag.slice(4)
31 | }
32 | // class 中增加 lazy
33 | newImgTag = newImgTag.replace(
34 | /(.*?) class="(.*?)" (.*?)>/gi,
35 | function (ori, before, value, after) {
36 | var newClass = value
37 | if (newClass.length > 0) {
38 | newClass += ' '
39 | }
40 | newClass += 'lazy'
41 | if (value) {
42 | return ori.replace('class="' + value, 'class="' + newClass)
43 | } else {
44 | return ori.replace('class="', 'class="' + newClass)
45 | }
46 | }
47 | )
48 | // 加载图
49 | const loadingImg =
50 | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABGdBTUEAALGPC/xhBQAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAADa6r/EAAAAC0lEQVQIHWNgAAIAAAUAAY27m/MAAAAASUVORK5CYII='
51 | newImgTag = newImgTag.replace(
52 | src_value,
53 | loadingImg + '" data-src="' + src_value
54 | )
55 | return newImgTag
56 | }
57 | )
58 | }
59 |
60 | module.exports.processSite = function (htmlContent) {
61 | return lazyProcess.call(this, htmlContent)
62 | }
63 |
--------------------------------------------------------------------------------
/scripts/tags/lib/frame.js:
--------------------------------------------------------------------------------
1 | /**
2 | * frame.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% frame iphone11 [img:src] [video:url] [focus:top/bottom] [alt] %}
6 | */
7 |
8 | 'use strict'
9 |
10 | module.exports = (ctx) =>
11 | function (args) {
12 | args = ctx.args.map(args, ['focus', 'img', 'video'], ['device', 'alt'])
13 | const img = args.img || ''
14 | const video = args.video || ''
15 | const device = args.device || ''
16 | const focus = args.focus || ''
17 | const alt = args.alt || ''
18 | if ((img.length == 0 && video.length == 0) || device.length == 0) {
19 | return
20 | }
21 | var el = ''
22 | function imgTag(url, alt) {
23 | let i = ''
24 | i += ' 0) {
26 | i += ' alt="' + alt + '"'
27 | }
28 | i += '/>'
29 | return i
30 | }
31 | if (video.length > 0) {
32 | el += ''
33 | el += '
0) {
35 | el += 'focus="' + focus + '">'
36 | } else {
37 | el += '>'
38 | }
39 | el += '
0) {
41 | el += ' poster="' + img + '"'
42 | }
43 | el += ' playsinline="" muted="" loop="" autoplay="" preload="metadata">'
44 | el += ''
45 | el += ' '
46 |
47 | el += '
'
48 | el += '
'
49 | el += '
'
50 | } else if (img.length > 0) {
51 | el += ''
52 | el += '
0) {
54 | el += 'focus="' + focus + '">'
55 | } else {
56 | el += '>'
57 | }
58 | el += imgTag(img, alt)
59 | el += '
'
60 | el += '
'
61 | if (alt.length > 0) {
62 | el += '
' + alt + ' '
63 | }
64 | el += '
'
65 | }
66 | return el
67 | }
68 |
--------------------------------------------------------------------------------
/scripts/tags/lib/tabs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * tabs.js v2 | 基于NexT修改: https://theme-next.js.org/docs/tag-plugins/tabs
3 | */
4 |
5 | 'use strict'
6 |
7 | var tab_index = 0
8 |
9 | module.exports = (ctx) =>
10 | function (args, content = '') {
11 | var arr = content
12 | .split(//g)
13 | .filter((item) => item.trim().length > 0)
14 | if (arr.length < 1) {
15 | return ''
16 | }
17 | var tabs = []
18 | arr.forEach((item, i) => {
19 | if (i % 2 == 0) {
20 | tabs.push({
21 | header: item,
22 | })
23 | } else if (tabs.length > 0) {
24 | var tab = tabs[tabs.length - 1]
25 | if (tab.body == undefined) {
26 | tab.body = item
27 | } else {
28 | tab.body += '\n' + item
29 | }
30 | }
31 | })
32 |
33 | args = ctx.args.map(args, ['active', 'align'])
34 | const tabName = 'tab_' + ++tab_index
35 | const tabActive = Number(args.active) || 0
36 |
37 | let tabId = 0
38 | let tabNav = ''
39 | let tabContent = ''
40 | tabs.forEach((tab, i) => {
41 | let content = ctx.render
42 | .renderSync({ text: tab.body || '', engine: 'markdown' })
43 | .trim()
44 | const abbr = tabName + ' ' + ++tabId
45 | const href = abbr.toLowerCase().split(' ').join('-')
46 | const isActive =
47 | (tabActive > 0 && tabActive === tabId) ||
48 | (tabActive === 0 && tabId === 1)
49 | ? ' active'
50 | : ''
51 | tabNav += ``
52 | tabContent += `${content}
`
53 | })
54 |
55 | tabNav = `${tabNav}
`
56 | tabContent = `${tabContent}
`
57 |
58 | var el = ''
59 | el += ''
65 | el += tabNav + tabContent
66 | el += '
'
67 | return el
68 | }
69 |
--------------------------------------------------------------------------------
/scripts/tags/lib/toc.js:
--------------------------------------------------------------------------------
1 | /**
2 | * toc.js v1 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% toc wiki:xxx [title] [open:true] [display:mobile] %}
6 | */
7 |
8 | 'use strict'
9 |
10 | function layoutDocTree(ctx, pages) {
11 | const url_for = require('hexo-util').url_for.bind(ctx)
12 | var el = ''
13 | el += ''
22 | return el
23 | }
24 |
25 | module.exports = (ctx) =>
26 | function (args) {
27 | args = ctx.args.map(args, ['wiki', 'open', 'display'], ['title'])
28 |
29 | var el = ''
30 | el += ''
36 |
37 | el += '
'
42 | el += ''
43 | el += args.title || 'TOC'
44 | el += ' '
45 |
46 | if (args.wiki) {
47 | const proj = ctx.theme.config.wiki.tree[args.wiki]
48 | if (proj == undefined) {
49 | return ''
50 | }
51 | if (proj.sections && proj.sections.length > 1) {
52 | el += ''
53 | proj.sections.forEach((sec, i) => {
54 | el += ''
55 | el += ''
58 | el += layoutDocTree(ctx, sec.pages)
59 | el += ' '
60 | })
61 | el += '
'
62 | } else {
63 | el += ''
64 | el += '
'
65 | el += layoutDocTree(ctx, proj.pages)
66 | el += '
'
67 | el += '
'
68 | }
69 | }
70 | el += ' '
71 | // end
72 | el += '
'
73 | return el
74 | }
75 |
--------------------------------------------------------------------------------
/layout/components/cover/wiki_cover.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const WikiCover = (props) => {
3 | const { theme, page, __, scroll_reveal } = props
4 | let proj = theme.wiki.tree[page.wiki]
5 | if (proj === undefined) {
6 | return <>>
7 | }
8 | if (proj.homepage.path !== page.path) {
9 | return <>>
10 | }
11 | if (proj.cover === undefined || proj.cover === false || proj.cover === '[]') {
12 | return <>>
13 | }
14 | let cover = proj.cover
15 | let logo = proj.logo
16 | let title = proj.title || page.title
17 | let desc = proj.description || page.description
18 | if (cover === true) {
19 | cover = ['logo', 'title', 'description']
20 | }
21 | return (
22 |
23 |
24 |
25 | {(() => {
26 | if (logo && logo.src && cover.includes('logo')) {
27 | let imageElement
28 | if (logo.large) {
29 | imageElement = (
30 |
31 | )
32 | } else {
33 | imageElement =
34 | }
35 | return {imageElement}
36 | }
37 | })()}
38 | {(() => {
39 | if (title && cover.includes('title')) {
40 | return (
41 |
42 | {title}
43 |
44 | )
45 | }
46 | })()}
47 | {(() => {
48 | if (desc && cover.includes('description')) {
49 | return {desc}
50 | }
51 | })()}
52 |
57 |
58 |
59 |
60 |
61 | )
62 | }
63 |
64 | module.exports = WikiCover
65 |
--------------------------------------------------------------------------------
/layout/components/main/navbar/list_categories.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 |
3 | const ListCategories = (props) => {
4 | const { url_for } = props
5 | let { categories, options } = props
6 | const { site } = props
7 | if (
8 | !options &&
9 | (!categories || !Object.prototype.hasOwnProperty.call(categories, 'length'))
10 | ) {
11 | options = categories
12 | categories = site.categories
13 | }
14 |
15 | if (!categories || !categories.length) return ''
16 | options = options || {}
17 |
18 | const separator =
19 | const suffix = ''
20 | const className = options.class || 'cap breadcrumb'
21 | const depth = options.depth ? parseInt(options.depth, 10) : 0
22 | const orderby = options.orderby || 'name'
23 | const order = options.order || 1
24 |
25 | const prepareQuery = (parent) => {
26 | const query = {}
27 |
28 | if (parent) {
29 | query.parent = parent
30 | } else {
31 | query.parent = { $exists: false }
32 | }
33 |
34 | return categories
35 | .find(query)
36 | .sort(orderby, order)
37 | .filter((cat) => cat.length)
38 | }
39 |
40 | const flatList = (level, parent) => {
41 | let result = []
42 |
43 | prepareQuery(parent).forEach((cat, i) => {
44 | if (i || level) result.push(separator)
45 | result.push(
46 |
51 | {cat.name}
52 |
53 | )
54 | if (!depth || level + 1 < depth) {
55 | result = result.concat(flatList(level + 1, cat._id))
56 | }
57 | })
58 |
59 | return result
60 | }
61 |
62 | return flatList(0)
63 | }
64 |
65 | const Category = (props) => {
66 | const { page } = props
67 | if (page.layout === 'post' && page.categories && page.categories.length > 0) {
68 | return (
69 |
70 |
71 |
72 |
73 | )
74 | } else {
75 | return <>>
76 | }
77 | }
78 |
79 | module.exports = Category
80 |
--------------------------------------------------------------------------------
/source/images/wechat.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/scripts/events/lib/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 部分代码借鉴自 NexT:
3 | * https://github.com/next-theme/hexo-theme-next/blob/master/scripts/events/lib/config.js
4 | * Volantis:
5 | * https://github.com/volantis-x/hexo-theme-volantis/blob/master/scripts/events/lib/cdn.js
6 | */
7 |
8 | 'use strict'
9 |
10 | const path = require('path')
11 |
12 | module.exports = (hexo) => {
13 | const { cache, language_switcher } = hexo.theme.config
14 | const warning = function (...args) {
15 | hexo.log.warn(
16 | `Since ${args[0]} is turned on, the ${args[1]} is disabled to avoid potential hazards.`
17 | )
18 | }
19 |
20 | if (cache && (cache.enable || cache.enabled) && language_switcher) {
21 | warning('language_switcher', 'caching')
22 | cache.enabled = false
23 | }
24 |
25 | if (cache && (cache.enable || cache.enabled) && hexo.config.relative_link) {
26 | warning('caching', '`relative_link` option in Hexo `_config.yml`')
27 | hexo.config.relative_link = false
28 | }
29 | hexo.config.meta_generator = false
30 |
31 | // merge data
32 | const data = hexo.locals.get('data')
33 | // merge widgets
34 | var widgets = hexo.render.renderSync({
35 | path: path.join(hexo.theme_dir, '_data/widgets.yml'),
36 | engine: 'yaml',
37 | })
38 | if (data.widgets) {
39 | for (let i of Object.keys(data.widgets)) {
40 | let widget = data.widgets[i]
41 | if (widget == null || widget.length == 0) {
42 | // delete
43 | delete widgets[i]
44 | } else {
45 | // create
46 | if (widgets[i] == null) {
47 | widgets[i] = widget
48 | } else {
49 | // merge
50 | for (let j of Object.keys(widget)) {
51 | widgets[i][j] = widget[j]
52 | }
53 | }
54 | }
55 | }
56 | }
57 | if (hexo.theme.config.data == undefined) {
58 | hexo.theme.config.data = {}
59 | }
60 | hexo.theme.config.data['widgets'] = widgets
61 |
62 | // default menu
63 | if (hexo.theme.config.sidebar.menu == undefined) {
64 | hexo.theme.config.sidebar.menu = []
65 | }
66 |
67 | hexo.on('server', () => {
68 | // detect server-side render
69 | hexo.log.info('Server-side render detected')
70 | hexo.theme.config.server_render.status = true
71 | })
72 | }
73 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/tabs.styl:
--------------------------------------------------------------------------------
1 | .tag-plugin.tabs
2 | position: relative
3 | margin-top: 1rem
4 | margin-bottom: 1rem
5 | display: flex
6 | flex-direction: column
7 | &[align=left]
8 | align-items: flex-start
9 | .nav-tabs
10 | align-self: flex-start
11 | &[align=center]
12 | align-items: center
13 | &[align=right]
14 | align-items: flex-end
15 | .nav-tabs
16 | align-self: flex-end
17 |
18 | .tag-plugin.tabs
19 | .nav-tabs
20 | display: flex
21 | align-self: center
22 | overflow: scroll visible
23 | scrollbar(0, 0)
24 | max-width: 100%
25 | white-space: nowrap
26 | margin: 0 !important
27 | line-height: 1.5
28 | position: relative
29 | padding: 8px 0
30 | &:after
31 | content: ''
32 | position: absolute
33 | background: var(--block-hover)
34 | width: 100%
35 | height: 2px
36 | bottom: 0
37 | left: 0
38 | border-radius: 2px
39 | .tab
40 | list-style-type: none
41 | a
42 | display: block
43 | cursor: pointer
44 | padding: .25rem .75rem
45 | font-size: $fs-13
46 | line-height: inherit
47 | font-weight: 500
48 | color: var(--text-p3)
49 | border-radius: 4px
50 | position: relative
51 | margin: 0 2px
52 | &:hover
53 | color: var(--text-p1)
54 | background: var(--block-hover)
55 | i
56 | pointer-events: none
57 | &.active a
58 | cursor: default
59 | color: var(--text-p1)
60 | background: var(--card)
61 | box-shadow: $boxshadow-button
62 | &:after
63 | content: ''
64 | z-index: 1
65 | position: absolute
66 | background: $color-theme
67 | height: 2px
68 | bottom: -8px
69 | left: .75rem
70 | right: .75rem
71 | border-radius: 2px
72 |
73 | .tab-content
74 | max-width: 100%
75 | text-align: justify
76 | margin-top: .5rem
77 | .tab-pane
78 | &:not(.active)
79 | display: none
80 | &.active
81 | display: block
82 |
83 | .md-text.indent .tag-plugin.tabs .tab-content p:not([class])
84 | text-indent: 'calc(%s * 2)' % $fs-p
85 | a
86 | text-indent: 0
--------------------------------------------------------------------------------
/source/css/_components/pages/archives.styl:
--------------------------------------------------------------------------------
1 | .post-list #archive
2 | padding: 1rem
3 | .archive-header
4 | display: inline-block
5 | font-family: $ff-code
6 | font-weight: 700
7 | line-height: 1.2
8 | margin-bottom: .5em
9 | position: relative
10 | padding: 4px 0
11 | color: var(--text-p3)
12 | &:after
13 | content: ""
14 | position: absolute
15 | height: 4px
16 | bottom: 0
17 | left: 0
18 | right: 0
19 | z-index: -1
20 | border-radius: 4px
21 | background: var(--block-border)
22 | a.post
23 | display: inline-flex
24 | align-items: baseline
25 | margin: 0.25rem 0
26 | color: var(--text-p1)
27 | &:hover
28 | color: $color-hover
29 | time
30 | font-family: $ff-code
31 | margin-right: 1em
32 | font-weight: 700
33 | flex-shrink: 0
34 | opacity .5
35 | &:hover
36 | .archive-header
37 | color: var(--text-p1)
38 | &:after
39 | background: $color-theme
40 |
41 |
42 |
43 | .post-list #cats
44 | padding: 0
45 | a.cat
46 | display: flex
47 | &.child
48 | padding-left: 2rem
49 | padding: .5em 1rem
50 | border-radius: $border-block
51 | color: var(--text-p2)
52 | align-items: center
53 | justify-content: space-between
54 | font-weight: 500
55 | font-size: $fs-14
56 | .badge
57 | font-weight: 700
58 | font-family: $ff-code
59 | opacity: .5
60 | font-size: $fs-12
61 | &:hover
62 | background: var(--block-hover)
63 | color: var(--text-p0)
64 | .badge
65 | opacity: 1
66 | color: $color-theme
67 |
68 | .post-list #tags
69 | display: flex
70 | flex-wrap: wrap
71 | padding: 0
72 | margin: 0 -4px
73 | a.tag
74 | display: inline-flex
75 | align-items: center
76 | position: relative
77 | color: var(--text-p2)
78 | margin: 4px
79 | padding: .5em .75rem
80 | border-radius: 4px
81 | background: var(--card)
82 | border: 1px solid var(--card-border)
83 | font-size: $fs-13
84 | font-weight: 500
85 | &:before
86 | content: "#"
87 | margin-left: -2px
88 | margin-right: 2px
89 | opacity: .4
90 | &:hover
91 | &:before
92 | color $color-theme
93 | opacity: 1
94 | color: var(--text-p0)
95 | background: var(--card)
96 |
--------------------------------------------------------------------------------
/source/css/_components/tag-plugins/link.styl:
--------------------------------------------------------------------------------
1 | .md-text .tag-plugin.link
2 | margin: 1em auto
3 | display: flex
4 | justify-content: center
5 |
6 | .md-text .link-card
7 | background: var(--card)
8 | display: inline-flex
9 | justify-content: space-between
10 | align-items: center
11 | cursor: pointer
12 | width: 320px
13 | max-width: 100%
14 | box-shadow: $boxshadow-card
15 | border-radius: 4px
16 | trans2: box-shadow transform
17 | &:hover
18 | box-shadow: $boxshadow-card-float
19 | transform: translateY(-1px)
20 |
21 | .md-text .link-card.plain
22 | flex-direction: row
23 | align-items: center
24 |
25 | .md-text .link-card.rich
26 | flex-direction: column
27 | align-items: stretch
28 | width: 460px
29 | text-align: justify
30 |
31 | .md-text .link-card
32 | >.left
33 | overflow: hidden
34 | margin: 0.75rem 0 0.75rem 0.75rem
35 | .title
36 | font-size: $fs-14
37 | span+span
38 | margin-top: 0.25rem
39 | >.right
40 | width: 2.75rem
41 | height: 2.75rem
42 | margin: 0.75rem
43 | overflow: hidden
44 | flex-shrink: 0
45 | >.top
46 | display: flex
47 | margin: 1rem 1rem 0.75rem
48 | overflow: hidden
49 | max-width: 'calc(100% - %s * 2)' % 1rem
50 | align-items: center
51 | .img
52 | line-height: 0
53 | height: 16px
54 | width: 16px
55 | min-width: 16px
56 | border-radius: 16px
57 | margin-right: 8px
58 | background-repeat: no-repeat
59 | background-size: contain
60 | background-position: left center
61 | span
62 | txt-ellipsis()
63 | max-width: 100%
64 | >.bottom
65 | margin: 0 1rem 1rem
66 | .title
67 | font-size: 1rem
68 | margin-bottom: 0.5rem
69 |
70 | .md-text .link-card
71 | line-height: 1.2
72 | .title
73 | display: -webkit-box
74 | -webkit-box-orient: vertical
75 | overflow: hidden
76 | -webkit-line-clamp: 2
77 | .cap
78 | flex-shrink: 0
79 | color: var(--text-p3)
80 | .link
81 | line-height: 1.5
82 | opacity: .75
83 | txt-ellipsis()
84 | .desc
85 | display: -webkit-box
86 | -webkit-box-orient: vertical
87 | overflow: hidden
88 | -webkit-line-clamp: 3
89 | .img
90 | border-radius: 4px
91 |
92 | span
93 | margin: 0
94 | display: block
95 | .title
96 | font-weight: 500
97 | color: var(--text-p1)
98 |
--------------------------------------------------------------------------------
/source/js/plugins/copycode.js:
--------------------------------------------------------------------------------
1 | ;(() => {
2 | const CopyCode = {
3 | copyCode: async function (codeCopyBtn, currentCode) {
4 | if (navigator.clipboard) {
5 | try {
6 | await navigator.clipboard.writeText(currentCode)
7 | } catch (error) {
8 | // 未获得用户许可
9 | codeCopyBtn.innerText = '未获得用户许可'
10 | codeCopyBtn.classList.add('warning')
11 | setTimeout(() => {
12 | codeCopyBtn.innerText = stellar.plugins.copycode.default_text
13 | codeCopyBtn.classList.remove('warning')
14 | }, 3000)
15 | }
16 | } else {
17 | codeCopyBtn.innerText = '当前浏览器不支持此API'
18 | codeCopyBtn.classList.add('warning')
19 | setTimeout(() => {
20 | codeCopyBtn.innerText = stellar.plugins.copycode.default_text
21 | codeCopyBtn.classList.remove('warning')
22 | }, 3000)
23 | }
24 | },
25 | init: function () {
26 | document.querySelectorAll('.code').forEach((codeElement) => {
27 | const codeElementStyle = window.getComputedStyle(
28 | codeElement,
29 | '::before'
30 | )
31 | const codeBeforeWidth = codeElementStyle.width.split('px')[0]
32 | const codeBeforePadding = codeElementStyle.padding
33 | .split(' ')
34 | .pop()
35 | .split('px')[0]
36 |
37 | // copy btn
38 | const codeCopyBtn = document.createElement('div')
39 | codeCopyBtn.classList.add('copy-btn')
40 | codeCopyBtn.style.right =
41 | Number(codeBeforeWidth) + Number(codeBeforePadding) * 2 + 'px'
42 | codeCopyBtn.innerText = stellar.plugins.copycode.default_text
43 |
44 | codeElement.appendChild(codeCopyBtn)
45 |
46 | codeCopyBtn.addEventListener('click', async function (evt) {
47 | const self = evt.target
48 | await CopyCode.copyCode(
49 | self,
50 | self.parentElement.children[0]?.innerText
51 | )
52 |
53 | self.innerText = stellar.plugins.copycode.success_text
54 | self.classList.add('success')
55 |
56 | setTimeout(() => {
57 | self.innerText = stellar.plugins.copycode.default_text
58 | self.classList.remove('success')
59 | }, 3000)
60 | })
61 | })
62 | },
63 | }
64 | stellaris.registerThemePlugin('.code', CopyCode)
65 | })()
66 |
--------------------------------------------------------------------------------
/source/css/_defines/theme.styl:
--------------------------------------------------------------------------------
1 | $color-theme = $light-color-theme //compat
2 |
3 | set_text_light()
4 | --text-p0: $dark-title
5 | --text-p1: mix($dark-text, $dark-mix, 85)
6 | --text-p2: mix($dark-text, $dark-mix, 70)
7 | --text-p3: mix($dark-text, $dark-mix, 45)
8 | --text-p4: mix($dark-text, $dark-mix, 35)
9 | --text-meta: mix($dark-text, $dark-mix, 30)
10 | --text-code: $dark-code
11 | --text-revert: $light-title
12 | set_text_dark()
13 | --text-p0: $light-title
14 | --text-p1: $light-text
15 | --text-p2: mix($light-text, $light-mix, 80)
16 | --text-p3: mix($light-text, $light-mix, 60)
17 | --text-p4: mix($light-text, $light-mix, 35)
18 | --text-meta: mix($light-text, $light-mix, 20)
19 | --text-code: $light-code
20 | --text-revert: $dark-title
21 | :root
22 | --site-bg: $light-background
23 | --color-scheme: $light-color-scheme
24 | --card-border: $light-border
25 | --card: $light-card
26 | --block: $light-block
27 | --block-border: darken($light-block, 5)
28 | --block-hover: darken($light-block, 2)
29 | set_text_dark()
30 | --theme-highlight: $light-color-theme
31 | --theme-cap: darken($light-color-theme,10)
32 | --theme-bg: $light-card
33 | --theme-link: $light-color-theme
34 |
35 | set_darkmode()
36 | --site-bg: $dark-background
37 | --color-scheme: $dark-color-scheme
38 | --card: $dark-card
39 | --card-border: $dark-border
40 | --block: $dark-block
41 | --block-border: lighten($dark-block, 6)
42 | --block-hover: darken($dark-block, 4)
43 | set_text_light()
44 | --theme-highlight: $dark-color-theme
45 | --theme-cap: $dark-color-theme
46 | --theme-bg: $dark-card
47 | @media screen and (max-width: $device-mobile-max)
48 | --site-bg: $dark-background-mobile
49 | --card: darken($dark-card, 6)
50 | --block: darken($dark-block, 4)
51 | --block-border: lighten($dark-block, 2)
52 | --block-hover: darken($dark-block,8)
53 | .social-wrap
54 | filter: invert(100%)
55 |
56 | if hexo-config('style.darkmode') == 'auto'
57 | // 系统的dark模式
58 | @media (prefers-color-scheme: dark)
59 | :root
60 | set_darkmode()
61 |
62 |
63 | if hexo-config('style.darkmode') == 'always'
64 | :root
65 | set_darkmode()
66 |
67 | if hexo-config('style.darkmode') == 'auto-switch'
68 | :root[data-theme="dark"]
69 | set_darkmode()
70 | :root[data-theme="auto"]
71 | @media (prefers-color-scheme: dark)
72 | set_darkmode()
73 |
--------------------------------------------------------------------------------
/source/css/_components/widgets/ghuser.styl:
--------------------------------------------------------------------------------
1 | .widget-wrapper.ghuser
2 | .widget-header+.widget-body
3 | margin: 0.75rem 0
4 | .widget-wrapper.ghuser .widget-body
5 | text-align: center
6 | background: var(--card)
7 | border-radius: $border-card
8 | padding: 1rem
9 | box-shadow: $boxshadow-card
10 |
11 | .widget-wrapper.ghuser
12 | .avatar
13 | display: block
14 | border-radius: 100%
15 | margin: 1rem auto 1.25rem auto
16 | max-width: 75%
17 | overflow: hidden
18 | img
19 | display: block
20 | @media screen and (max-width: $device-tablet)
21 | max-width: 50%
22 |
23 | .username
24 | font-weight: 900
25 | font-size: $fs-h2
26 | color: var(--text-p0)
27 | margin: 0.5rem 0
28 |
29 | .bio
30 | font-size: $fs-13
31 | margin: 0.5rem 0
32 |
33 | .follow
34 | font-weight: 500
35 | border-radius: 64px
36 | padding: 0.5rem 1rem
37 | background: $color-theme
38 | color: var(--card)
39 | font-size: 1rem
40 | align-self: stretch
41 | text-align: center
42 | line-height: 1.5
43 | display: flex
44 | align-items: center
45 | justify-content: center
46 | trans1: background
47 | svg
48 | margin-right: 6px
49 | &:hover
50 | background: $color-hover
51 |
52 | .menu
53 | margin-bottom: 0
54 | background: none
55 | a:hover
56 | box-shadow: none
57 | background: var(--block)
58 | a.active
59 | box-shadow: none
60 | position: relative
61 | &:after
62 | content: ''
63 | position: absolute
64 | height: 3px
65 | bottom: 0
66 | width: 32px
67 | left: 'calc(50% - 0.5 * %s)' % @width
68 | border-radius: 4px
69 | background: $color-theme
70 |
71 | .buttons
72 | margin: 1rem 0
73 | align-self: stretch
74 | display: grid
75 | grid-gap: 2px
76 | grid-template-columns: repeat(auto-fill, "calc((100% - 2 * %s) / 3)" % 2px)
77 |
78 | .btn
79 | display: flex
80 | flex-direction: column
81 | align-items: center
82 | color: inherit
83 | border: 1px solid transparent
84 | border-radius: 4px
85 | padding: 0.25rem 0
86 | trans1: background
87 | &:hover
88 | background: var(--block)
89 | .title
90 | font-size: 1rem
91 | font-weight: 700
92 | .desc
93 | font-size: $fs-12
94 | color: var(--text-p3)
95 | font-weight: 500
--------------------------------------------------------------------------------
/source/js/plugins/linkcard.js:
--------------------------------------------------------------------------------
1 | // 本插件由CardLink定制而成,原项目源码: https://github.com/Lete114/CardLink
2 | ;(() => {
3 | const selector = 'a.link-card[cardlink]'
4 | const CardLink = {
5 | renderer: function (el, obj) {
6 | var autofill = []
7 | const autofillStr = el.getAttribute('autofill')
8 | if (autofillStr) {
9 | autofill = autofillStr.split(',')
10 | }
11 | if (obj.title && obj.title.length > 0 && autofill.includes('title')) {
12 | el.querySelector('.title').innerHTML = obj.title
13 | el.title = obj.title
14 | }
15 | if (obj.icon && obj.icon.length > 0 && autofill.includes('icon')) {
16 | el.querySelector('.img').style =
17 | 'background-image: url("' + obj.icon + '");'
18 | el.querySelector('.img').setAttribute('data-bg', obj.icon)
19 | }
20 | let desc = el.querySelector('.desc')
21 | if (
22 | desc &&
23 | obj.desc &&
24 | obj.desc.length > 0 &&
25 | autofill.includes('desc')
26 | ) {
27 | desc.innerHTML = obj.desc
28 | }
29 | },
30 | /**
31 | * Create card links
32 | * @param {NodeList} nodes A collection of nodes or a collection of arrays,
33 | * if it is an array then the array must always contain node element
34 | */
35 | setCardLink: function (nodes) {
36 | // If the `nodes` do not contain a `forEach` method, then the default `a[cardlink]` is used
37 | nodes =
38 | 'forEach' in (nodes || {})
39 | ? nodes
40 | : document.querySelectorAll('a[cardlink]')
41 | nodes.forEach((el) => {
42 | // If it is not a tag element then it is not processed
43 | if (el.nodeType !== 1) return
44 | el.removeAttribute('cardlink')
45 | const api = el.getAttribute('api')
46 | fetch(api)
47 | .then(function (response) {
48 | if (response.ok) {
49 | return response.json()
50 | }
51 | throw new Error('Network response was not ok.')
52 | })
53 | .then(function (data) {
54 | CardLink.renderer(el, data)
55 | })
56 | .catch(function (error) {
57 | console.log(error)
58 | })
59 | })
60 | },
61 | init: function () {
62 | this.setCardLink(document.querySelectorAll(selector))
63 | },
64 | }
65 | stellaris.registerThemePlugin(selector, CardLink)
66 | })()
67 |
--------------------------------------------------------------------------------
/source/css/_plugins/search/local-search.styl:
--------------------------------------------------------------------------------
1 | .search-wrapper
2 | width: 100%
3 | >.search-form
4 | position: sticky
5 | top: 1rem
6 | .search-input
7 | width: 100%
8 | padding: 0.5rem 0.5rem
9 | line-height: 1
10 | box-sizing: border-box
11 | border-radius: 4px
12 | border: 1px solid var(--card-border)
13 | background-color: var(--card)
14 | color: var(--text-p0)
15 | //box-shadow: $boxshadow-button
16 | trans2 border-bottom-color border-bottom-width
17 | &:focus
18 | border-bottom-width: 2px
19 | border-bottom-color: var(--theme-highlight)
20 | border-bottom: 1px solid grey
21 |
22 | &.noresult
23 | trans1 border-bottom-color
24 | .search-input
25 | &:hover
26 | border-bottom-color: darken($c-red,30%)
27 | &:focus
28 | border-bottom-color: darken($c-red,30%)
29 |
30 | .search-no-result
31 | display: none
32 | margin: 1em auto
33 | color: var(--text-p1)
34 | text-align: center
35 | font-size: $fs-14
36 | padding: 2rem
37 | //background: var(--block)
38 | border-radius: $border-block
39 |
40 | #search-result
41 | ul.search-result-list
42 | padding: 0
43 | margin: 0
44 | list-style-type: none
45 | li
46 | margin: 1em auto
47 | &:hover
48 | .search-result-title
49 | color: $color-hover
50 | .search-result-title
51 | background-image: none
52 | color: var(--text-p1)
53 | font-weight: bold
54 | line-height: 1.2
55 |
56 | .search-result-content
57 | overflow: hidden
58 | color: var(--text-p3)
59 | margin: .4em auto
60 | max-height: 13em
61 | text-align: justify
62 | font-size: $fs-12
63 | line-height: 1.2
64 | display: -webkit-box
65 | -webkit-box-orient: vertical
66 | overflow: hidden
67 | -webkit-line-clamp: 3
68 |
69 | .search-keyword
70 | border-bottom: 1px dashed $color-hover
71 | color: $color-hover
72 | font-weight: bold
73 |
74 |
75 |
76 | .search-wrapper.noresult
77 | .search-no-result
78 | display: block
79 |
80 | .widget-wrapper
81 | .search-form
82 | top: 0
83 | //background: var(--site-bg)
84 | .search-input
85 | margin-top: 1rem
86 | margin-bottom: -1rem
87 | #search-result,.search-no-result
88 | margin-top: 2rem
89 |
90 | .widget-wrapper:not(:first-child)
91 | .search-wrapper
92 | margin-top: -1rem
93 |
--------------------------------------------------------------------------------
/layout/components/main/article/read_next.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const ReadNext = (props) => {
3 | const { page, config, theme, url_for, date, __ } = props
4 | let prev, next
5 | let title_prev = __('meta.prev')
6 | let title_next = __('meta.next')
7 | if (page.layout === 'post') {
8 | prev = page.prev
9 | next = page.next
10 | title_prev = __('meta.newer')
11 | title_next = __('meta.older')
12 | } else if (page.layout === 'wiki' && page.wiki && page.wiki.length > 0) {
13 | let proj = theme.wiki.tree[page.wiki]
14 | if (proj) {
15 | let ps = proj.pages?.filter((p) => p.path == page.path)
16 | if (ps?.length > 0) {
17 | const current_page_number = ps[0].page_number || 0
18 | proj.pages.forEach((p, i) => {
19 | if (p.page_number < current_page_number) {
20 | if (prev == undefined || p.page_number > prev.page_number) {
21 | prev = p
22 | }
23 | } else if (p.page_number > current_page_number) {
24 | if (next == undefined || p.page_number < next.page_number) {
25 | next = p
26 | }
27 | }
28 | })
29 | } else {
30 | console.error('未找到当前页')
31 | }
32 | }
33 | }
34 |
35 | if (prev || next) {
36 | return (
37 |
67 | )
68 | } else return <>>
69 | }
70 |
71 | module.exports = ReadNext
72 |
--------------------------------------------------------------------------------
/layout/components/plugins/mermaid/script.jsx:
--------------------------------------------------------------------------------
1 | const MermaidScripts = (props) => {
2 | const blogTheme = props.theme
3 | const { enabled, js, theme } = blogTheme.plugins.mermaid
4 |
5 | if (enabled) {
6 | const loadMermaidScript = `
7 | async function loadMermaid() {
8 | try {
9 | console.log("Mermaid enabled");
10 |
11 | // Load Mermaid script
12 | await stellar.loadScript("${js}", { defer: true });
13 |
14 | // Initialize Mermaid
15 | mermaid.initialize({
16 | startOnLoad: false,
17 | theme:
18 | "${blogTheme.style.darkmode}".startsWith("auto")
19 | ? window.matchMedia("(prefers-color-scheme: dark)").matches
20 | ? "dark"
21 | : "${theme}"
22 | : "${theme}",
23 | // logLevel: 3,
24 | flowchart: {
25 | useMaxWidth: false,
26 | htmlLabels: true,
27 | curve: "linear",
28 | },
29 | gantt: {
30 | axisFormat: "%Y/%m/%d",
31 | },
32 | sequence: {
33 | actorMargin: 50,
34 | },
35 | });
36 |
37 | // Select the non-processed Mermaid elements
38 | const mermaidElements = document.querySelectorAll('pre.mermaid:not([data-processed="true"])') || [];
39 | for (const ele of mermaidElements) {
40 | try {
41 | // Wait for the element to be fully loaded
42 | await new Promise((resolve) => {
43 | const interval = setInterval(() => {
44 | if (ele.innerText.trim() !== "") {
45 | clearInterval(interval);
46 | resolve();
47 | }
48 | }, 20);
49 | });
50 |
51 | // Render Mermaid Graph
52 | const uniqueId = "mermaidGraph_" + Math.random().toString(36).substr(2, 9);
53 | ele.setAttribute("data-processed", "true");
54 | // print the graph definition
55 | // console.log("Rendering Mermaid element:\\n", ele.innerText);
56 | const { svg } = await mermaid.render(uniqueId, ele.innerText);
57 | ele.innerHTML = svg;
58 | } catch (renderErr) {
59 | console.error("Error rendering Mermaid element:", renderErr, ele);
60 | }
61 | }
62 | } catch (err) {
63 | console.error("Error initializing Mermaid:", err);
64 | }
65 | }
66 | try {
67 | loadMermaid();
68 | } catch (e) {
69 | console.error("Error initializing Mermaid:", e);
70 | }
71 | `
72 | return
73 | } else {
74 | return <>>
75 | }
76 | }
77 |
78 | module.exports = MermaidScripts
79 |
--------------------------------------------------------------------------------
/scripts/tags/lib/sites.js:
--------------------------------------------------------------------------------
1 | /**
2 | * sites.js v2 | https://github.com/xaoxuu/hexo-theme-stellar/
3 | * 格式与官方标签插件一致使用空格分隔,中括号内的是可选参数(中括号不需要写出来)
4 | *
5 | * {% sites [group] [repo:owner/repo] [api:http] %}
6 | */
7 |
8 | 'use strict'
9 |
10 | module.exports = (ctx) =>
11 | function (args) {
12 | args = ctx.args.map(args, ['repo', 'api'], ['group'])
13 | var links = ctx.locals.get('data').links
14 | if (links == undefined) {
15 | links = {}
16 | }
17 | var api
18 | if (args.api) {
19 | api = args.api
20 | } else if (args.repo) {
21 | api = 'https://api.vlts.cc/output_data/v2/' + args.repo
22 | }
23 |
24 | var el = ''
25 | if (api) {
26 | el += '
'
29 | el += '
'
30 | el += '
'
31 | } else if (args.group) {
32 | function cell(item) {
33 | if (item.url && item.title) {
34 | var cell = '
'
60 | return cell
61 | } else {
62 | return ''
63 | }
64 | }
65 | el += '
'
66 | const items = links[args.group] || []
67 | items.forEach((item, i) => {
68 | el += cell(item)
69 | })
70 | el += '
'
71 | }
72 |
73 | el += '
'
74 | return el
75 | }
76 |
--------------------------------------------------------------------------------
/source/css/_components/partial/related.styl:
--------------------------------------------------------------------------------
1 | .related-wrap
2 | padding: 1rem
3 | margin: 2rem 0
4 | &:empty
5 | display: none
6 | section.header
7 | display: flex
8 | justify-content: space-between
9 | align-items: center
10 | >span.title
11 | padding: 0.25rem 0
12 | section.footer
13 | margin-top: 1rem
14 |
15 | a.more
16 | padding: 0.25rem 0.5rem
17 | border-radius: $border-block
18 | color: var(--text-p1)
19 |
20 |
21 |
22 | .related-posts
23 | max-width: 100%
24 | margin: 1rem 0
25 | .item
26 | line-height: 1.2
27 | display: block
28 | border-left: 0
29 | margin-top: 1rem
30 | .title
31 | color: var(--text-p1)
32 | font-weight: 500
33 | font-size: 1rem
34 | trans2 color border
35 | position: relative
36 | padding-bottom: 2px
37 | border-bottom: 1px dashed var(--text-meta)
38 | line-height: 1.6
39 | .excerpt
40 | color: var(--text-p3)
41 | font-size: $fs-12
42 | margin-top: 0.5rem
43 | display: -webkit-box
44 | -webkit-box-orient: vertical
45 | overflow: hidden
46 | -webkit-line-clamp: 2
47 | &:hover
48 | .title
49 | color: $color-hover
50 | border-bottom: 1px solid $color-hover
51 |
52 |
53 | .related-wrap#read-next
54 | .body
55 | display: grid
56 | grid-gap: 16px
57 | grid-template-columns: repeat(auto-fill, "calc((100% - 1 * %s) / 2)" % 16px)
58 | .item
59 | border-top: 1px dashed var(--block-border)
60 | border-bottom: 1px dashed var(--block-border)
61 | padding: 1rem 0
62 | .note
63 | margin-bottom: 0.75rem
64 | font-size: $fs-12
65 | color: var(--text-p4)
66 | font-weight: 500
67 | a
68 | margin: 0
69 | line-height: 1.2
70 | color: var(--text-p1)
71 | font-size: $fs-h4
72 | &:hover
73 | color: $color-hover !important
74 | #next
75 | text-align: right
76 | .wiki+.related-wrap#read-next
77 | .item
78 | a
79 | font-size: $fs-h3
80 | prev a
81 | color: var(--text-p3)
82 |
83 |
84 | .related-wrap#comments
85 | div.cmt-title
86 | p
87 | margin: 0
88 | font-size: inherit
89 | a
90 | color: inherit
91 | text-decoration: underline
92 | &:hover
93 | color: $color-hover
94 | &:after
95 | display: none
96 | div.cmt-body
97 | min-height: 150px
98 | position: relative
99 | svg.loading
100 | top: 60px
101 |
--------------------------------------------------------------------------------
/_data/widgets.yml:
--------------------------------------------------------------------------------
1 | # 您可以在这里创建属于自己的侧边栏组件库,在需要的位置放置自己的组件
2 | # layout即组件布局,支持自定义的有:
3 | # - markdown: 渲染 md 文本
4 | #
5 | search_blog:
6 | layout: search
7 | filter: auto # auto or 'path'
8 | placeholder: 文章搜索
9 |
10 | search_docs:
11 | layout: search
12 | filter: /wiki/ # auto or 'path'
13 | placeholder: 文档搜索
14 |
15 | ghrepo:
16 | layout: ghrepo
17 | related:
18 | layout: related
19 |
20 | ghissues:
21 | layout: ghissues
22 | title: # Recent Issues
23 | limit: 3 # 显示的issues最大数量
24 | labels: # 过滤只显示具有某些标签的issues
25 |
26 | # 将其覆盖设置为空,可删除一个默认组件,例如 welcome:
27 | # Recent update
28 | recent:
29 | layout: recent
30 | rss: # /atom.xml # npm i hexo-generator-feed
31 | limit: 5 # Count of posts
32 | # TOC (valid only in layout:post/wiki)
33 | toc:
34 | layout: toc
35 | list_number: false
36 | min_depth: 2
37 | max_depth: 5
38 | fallback: recent # Use a backup widget when toc does not exist.
39 | # github user info
40 | ghuser:
41 | layout: ghuser
42 | username: github # your github login username
43 | avatar: true # show avatar or not
44 | menu: true # show menu or not
45 |
46 | tagcloud:
47 | layout: tagcloud
48 | title: 标签云
49 | # 标签云配置
50 | min_font: 12
51 | max_font: 24
52 | amount: 100
53 | orderby: name
54 | order: 1 # 1, sac 升序;-1, desc 降序
55 | color: false # 使用颜色
56 | start_color: # 开始的颜色。您可使用十六进位值(#b700ff),rgba(rgba(183, 0, 255, 1)),hsla(hsla(283, 100%, 50%, 1))或 颜色关键字。此变量仅在 color 参数开启时才有用。
57 | end_color: # 结束的颜色。您可使用十六进位值(#b700ff),rgba(rgba(183, 0, 255, 1)),hsla(hsla(283, 100%, 50%, 1))或 颜色关键字。此变量仅在 color 参数开启时才有用。
58 | show_count: false # 显示每个标签的文章总数
59 |
60 | welcome:
61 | layout: markdown
62 | title: Stellaris 入门指南
63 | content: | # support markdown
64 | 欢迎使用 [Stellaris](https://github.com/chiyuki0325/hexo-theme-stellaris/) 主题,下面是您的入门指南,祝您使用愉快!
65 |
66 | **第一步**
67 | 创建 `blog/_config.stellaris.yml` 文件,在此文件中填写需要自定义的主题配置。
68 |
69 | **第二步**
70 | 创建 `blog/source/_data/widgets.yml` 文件,此文件中填写需要自定义的侧边栏组件,例如 `welcome` 组件。
71 |
72 | 如果有任何疑问,请先查阅 [示例配置文件](https://github.com/chiyuki0325/hexo-theme-stellaris/blob/main/_config.yml) 和 [原主题文档](https://xaoxuu.com/wiki/stellar/),如果文档中没有提供,请提 [issue](https://github.com/chiyuki0325/hexo-theme-stellaris/issues/) 向开发中询问。
73 |
74 | ### Stellaris 中的 timeline 并没有进行维护,不一定可用! ###
75 |
76 | timeline:
77 | layout: timeline
78 | title: 近期动态
79 | api: # https://api.github.com/repos/xaoxuu/hexo-theme-stellar/issues
80 | user: # 默认显示所有人的数据,设置名称可过滤为仅显示某人的数据,多个名称用英文逗号隔开,不要加空格
81 | type: # 默认不用写,如果是友链朋友圈数据请写 fcircle
82 | limit: # 默认通过 api 上增加 per_page 来设置,如果是友链朋友圈,可通过这个设置数量
83 |
--------------------------------------------------------------------------------
/layout/archive.jsx:
--------------------------------------------------------------------------------
1 | const { Fragment } = require('react')
2 | const Index = require('./index.jsx')
3 | const NavBarListPost = require('./components/main/navbar/list_post.jsx')
4 | const Posts = (props) => {
5 | const Year = (props) => {
6 | const YearPosts = (props) => {
7 | const Post = (props) => {
8 | const { config, url_for, post, date } = props
9 | return (
10 |
18 | )
19 | }
20 | const { site, date } = props
21 | const elements = []
22 | site.posts
23 | .sort('date', -1)
24 | .filter((post) => {
25 | post.year = date(post.date, 'YYYY')
26 | return post.year === year
27 | })
28 | .each((post) => {
29 | elements.push( )
30 | })
31 | return elements
32 | }
33 | const { scroll_reveal, year } = props
34 | return (
35 |
36 | {year}
37 |
38 |
39 | )
40 | }
41 |
42 | const { site, date } = props
43 | const elements = []
44 |
45 | const years = []
46 | site.posts.sort('date', -1).each(function (post) {
47 | post.year = date(post.date, 'YYYY')
48 | if (
49 | post.year &&
50 | years.includes(post.year) === false &&
51 | (post.title || post.date)
52 | ) {
53 | years.push(post.year)
54 | }
55 | })
56 |
57 | years.forEach((year) => {
58 | elements.push( )
59 | })
60 |
61 | return elements
62 | }
63 |
64 | const Archive = (props) => {
65 | let { page } = props
66 | const { __, is_category, is_tag } = props
67 | page.robots = 'noindex,follow'
68 | if (page.menu_id === undefined) {
69 | page.menu_id = 'post'
70 | }
71 | if (page.posts && (is_category() || is_tag())) {
72 | return
73 | } else {
74 | // archive page
75 | page.menu_id = 'archive'
76 | page.title = __('btn.archives')
77 | return (
78 |
79 |
80 |
83 |
84 | )
85 | }
86 | }
87 |
88 | module.exports = Archive
89 |
--------------------------------------------------------------------------------