├── .gitignore ├── LICENSE ├── README.md ├── assets ├── fonts │ ├── google │ │ ├── bbr.woff2 │ │ ├── ir.woff2 │ │ ├── jsr.woff2 │ │ ├── lobster.woff2 │ │ ├── ml.woff2 │ │ └── sarpanch.woff2 │ └── icomoon │ │ └── Icon.woff └── images │ └── bg.png ├── blog ├── 404 │ └── index.md ├── .vuepress │ ├── __tests__ │ │ ├── snippet-with-region.js │ │ └── snippet.js │ ├── config.js │ ├── config │ │ └── themeConfig.js │ ├── enhanceApp.js │ └── public │ │ ├── images │ │ ├── friend.jpg │ │ └── screenshot.webp │ │ └── logo.jpg ├── README.md ├── _post │ ├── maker.md │ ├── md5.md │ ├── theme-learning-0.md │ ├── theme-learning-concept.md │ └── theme-showcase.md └── friend-links │ └── index.md ├── components ├── Archive.vue ├── Category.vue ├── CategoryItem.vue ├── ColorScheme.vue ├── Comments.vue ├── DarkMode.vue ├── DropdownLink.vue ├── DropdownTransition.vue ├── FooterBar.vue ├── FriendLink.vue ├── Home.vue ├── NavLink.vue ├── NavLinks.vue ├── Navbar.vue ├── Post.vue ├── PostMeta.vue ├── PostNav.vue ├── PostTag.vue ├── Reward.vue ├── SettingPanel.vue ├── SideBar.vue ├── Sticker.vue ├── SubNav.vue ├── SvgSprite.vue ├── Tag.vue ├── TagItem.vue ├── Toc.vue └── Valine.vue ├── deploy.sh ├── global-components ├── Badge.vue ├── Icon.vue ├── RelatedPosts.vue └── ThemeSWUpdatePopup.vue ├── index.js ├── layouts ├── 404.vue └── Layout.vue ├── mixins └── index.js ├── package.json ├── plugin ├── demo-code │ ├── DemoCode.vue │ ├── enhanceAppFile.js │ └── index.js ├── float-menu │ ├── FloatMenu.vue │ ├── Search.vue │ ├── enhanceAppFile.js │ └── index.js ├── theme-palette │ ├── ThemePalette.vue │ ├── enhanceAppFile.js │ └── index.js └── theme-utils │ └── index.js ├── styles ├── code.styl ├── color_scheme.styl ├── custom_block.styl ├── fonts.styl ├── index.styl ├── mobile.styl └── palette.styl ├── templates ├── dev.html └── ssr.html └── util ├── index.js └── node.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode 3 | blog/.vuepress/dist -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-present, neil chen 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vuepress-theme-maker 2 | 3 |

4 | 5 |

6 | 7 | A flat and clean Blog Theme for VuePress site. inspired by [Hexo-theme-inside](https://github.com/ikeq/hexo-theme-inside) 8 | 9 | ## Features 10 | 11 | - Build with Vue.js 12 | - 在VuePress默认主题基础之上修改,保留了内置搜索,tag分类等绝大多数特性 13 | - 基于官方博客插件`@vuepress/plugin-blog`制作 14 | - 扩展了Markdown语法,支持`footnote` `mark` `abbr` `task-lists`, `Demo Code` 15 | - 支持文章评论 16 | - 文章打赏模块 17 | - 内置友情链接页 18 | - 文章阅读时长估算 19 | - 文章阅读计数(依赖valine评论) 20 | - rss订阅及sitemap 21 | - 访客自定义主题 22 | - 导航多级菜单 23 | - PWA支持 24 | - seo配置 25 | - 代码复制 26 | - 暗黑模式 27 | - 响应式主题 28 | 29 | ## Install 30 | 31 | ``` 32 | npm i vuepress-theme-maker -D 33 | # OR yarn add vuepress-theme-maker -D 34 | ``` 35 | 36 | ## Usage 37 | 38 | ``` 39 | // .vuepress/config.js 40 | module.exports = { 41 | theme: 'vuepress-theme-maker', 42 | themeConfig: { 43 | // Please head documentation to see the available options. 44 | } 45 | } 46 | ``` 47 | 48 | For more details, see [Theme Doc](https://80shuo.com/post/2020/12/23/maker.html) 49 | 50 | ## Demo 51 | 52 | [80shuo.com](https://github.com/80maker/80shuo) 53 | 54 | ## Screenshot 55 | 56 | ![](https://80shuo.com/images/screenshot-1.jpg) 57 | 58 | ![](https://80shuo.com/images/screenshot-2.jpg) 59 | 60 | ![](https://80shuo.com/images/screenshot-3.jpg) 61 | 62 | ## Deploy to Github Pages 63 | 64 | ``` 65 | npm run deploy 66 | ``` 67 | 68 | ## Feedback 69 | 70 | Feedback to developer: https://github.com/80maker/vuepress-theme-maker/issues 71 | ## Thanks to 72 | 73 | - [VuePress](https://vuepress.vuejs.org/) 74 | - [@vuepress/plugin-blog](https://github.com/vuepress/vuepress-plugin-blog) 75 | - [@vuepress/theme-blog](https://github.com/vuepress/vuepress-theme-blog) 76 | 77 | ### license 78 | [MIT](https://github.com/80maker/vuepress-theme-maker/blob/master/LICENSE) 79 | -------------------------------------------------------------------------------- /assets/fonts/google/bbr.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/assets/fonts/google/bbr.woff2 -------------------------------------------------------------------------------- /assets/fonts/google/ir.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/assets/fonts/google/ir.woff2 -------------------------------------------------------------------------------- /assets/fonts/google/jsr.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/assets/fonts/google/jsr.woff2 -------------------------------------------------------------------------------- /assets/fonts/google/lobster.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/assets/fonts/google/lobster.woff2 -------------------------------------------------------------------------------- /assets/fonts/google/ml.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/assets/fonts/google/ml.woff2 -------------------------------------------------------------------------------- /assets/fonts/google/sarpanch.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/assets/fonts/google/sarpanch.woff2 -------------------------------------------------------------------------------- /assets/fonts/icomoon/Icon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/assets/fonts/icomoon/Icon.woff -------------------------------------------------------------------------------- /assets/images/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/assets/images/bg.png -------------------------------------------------------------------------------- /blog/.vuepress/__tests__/snippet-with-region.js: -------------------------------------------------------------------------------- 1 | // #region snippet 2 | function foo () { 3 | return ({ 4 | dest: '../../vuepress', 5 | locales: { 6 | '/': { 7 | lang: 'en-US', 8 | title: 'VuePress', 9 | description: 'Vue-powered Static Site Generator' 10 | }, 11 | '/zh/': { 12 | lang: 'zh-CN', 13 | title: 'VuePress', 14 | description: 'Vue 驱动的静态网站生成器' 15 | } 16 | }, 17 | head: [ 18 | ['link', { rel: 'icon', href: `/logo.png` }], 19 | ['link', { rel: 'manifest', href: '/manifest.json' }], 20 | ['meta', { name: 'theme-color', content: '#3eaf7c' }], 21 | ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }], 22 | ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }], 23 | ['link', { rel: 'apple-touch-icon', href: `/icons/apple-touch-icon-152x152.png` }], 24 | ['link', { rel: 'mask-icon', href: '/icons/safari-pinned-tab.svg', color: '#3eaf7c' }], 25 | ['meta', { name: 'msapplication-TileImage', content: '/icons/msapplication-icon-144x144.png' }], 26 | ['meta', { name: 'msapplication-TileColor', content: '#000000' }] 27 | ] 28 | }) 29 | } 30 | // #endregion snippet 31 | 32 | export default foo -------------------------------------------------------------------------------- /blog/.vuepress/__tests__/snippet.js: -------------------------------------------------------------------------------- 1 | export default function () { 2 | // .. 3 | } -------------------------------------------------------------------------------- /blog/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: '1980\'s Maker', 3 | description: '一个出生于80年代的程序员 -- 喜爱创[客]、美[食]、动[画]、怀[旧]的新手艺人', 4 | port: 8088, 5 | base: '/blog/', 6 | markdown: { 7 | lineNumbers: true, 8 | extractHeaders: [ 'h2', 'h3', 'h4' ], 9 | plugins: { 10 | 'markdown-it-mark': true, 11 | 'markdown-it-footnote': true, 12 | 'markdown-it-abbr': true, 13 | 'markdown-it-task-lists': true 14 | } 15 | }, 16 | theme: require.resolve('../../index'), // 使用本地主题 17 | themeConfig: require('./config/themeConfig') 18 | } -------------------------------------------------------------------------------- /blog/.vuepress/config/themeConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | siteName: '1980\'s Maker', 3 | logo: '/logo.jpg', 4 | siteDesc: '一个出生于80年代的程序员 -- 喜爱创[客]、美[食]、动[画]、怀[旧]的新手艺人', 5 | nav: [ 6 | { text: '🏠 Home', link: '/' }, 7 | { text: '📖 Theme', link: '/categories/theme/' }, 8 | { text: '🐉 Maker', link: '/categories/maker/' }, 9 | { text: '🔥 Animation', link: '/categories/animation/' }, 10 | { text: '📽 Old Time', link: '/categories/oldtime/' }, 11 | { text: '🔗 friend-links', link: '/friend-links/' }, 12 | ], 13 | searchPlaceholder: 'Search', 14 | searchMaxSuggestions: 10, 15 | social: [ 16 | { 17 | type: 'email', 18 | link: 'cmgddd@163.com' 19 | }, 20 | { 21 | type: 'github', 22 | link: '80maker/vuepress-theme-maker' 23 | }, 24 | { 25 | type: 'qq', 26 | link: '//qm.qq.com/cgi-bin/qm/qr?k=fknyQ434nkzVUWUmJ6rpIPctkS9eyQaZ&jump_from=webapi' 27 | }, 28 | { 29 | type: 'feed', 30 | link: '/rss.xml' 31 | } 32 | ], 33 | copyright: '© 2020 ❤️ Neil Chen', 34 | blog: { 35 | directories: [ 36 | { 37 | id: 'post', 38 | dirname: '_post', 39 | path: '/', 40 | itemPermalink: '/post/:year/:month/:day/:slug.html', 41 | frontmatter: { title: '' }, 42 | pagination: { 43 | perPagePosts: 10, 44 | prevText: '', 45 | nextText: '' 46 | }, 47 | } 48 | ], 49 | frontmatters: [ 50 | { 51 | id: "tag", 52 | keys: ['tag', 'tags'], 53 | path: '/tags/', 54 | frontmatter: { title: 'Tag' }, 55 | pagination: { 56 | lengthPerPage: 10, 57 | prevText: '', 58 | nextText: '' 59 | } 60 | }, 61 | { 62 | id: "category", 63 | keys: ['category', 'categories'], 64 | path: '/categories/', 65 | frontmatter: { title: 'Category' }, 66 | pagination: { 67 | lengthPerPage: 10, 68 | prevText: '', 69 | nextText: '' 70 | } 71 | } 72 | ], 73 | sitemap: { 74 | hostname: 'https://80shuo.com', 75 | exclude: ['/404.html'] 76 | }, 77 | feed: { 78 | canonical_base: 'http://80shuo.com', 79 | }, 80 | palette: {}, 81 | comment: {} 82 | } 83 | } -------------------------------------------------------------------------------- /blog/.vuepress/enhanceApp.js: -------------------------------------------------------------------------------- 1 | // 使用异步函数也是可以的 2 | export default ({ 3 | Vue, // VuePress 正在使用的 Vue 构造函数 4 | options, // 附加到根实例的一些选项 5 | router, // 当前应用的路由实例 6 | siteData, // 站点元数据 7 | isServer // 当前应用配置是处于 服务端渲染 或 客户端 8 | }) => { 9 | // ...做一些其他的应用级别的优化 10 | } -------------------------------------------------------------------------------- /blog/.vuepress/public/images/friend.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/blog/.vuepress/public/images/friend.jpg -------------------------------------------------------------------------------- /blog/.vuepress/public/images/screenshot.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/blog/.vuepress/public/images/screenshot.webp -------------------------------------------------------------------------------- /blog/.vuepress/public/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/blog/.vuepress/public/logo.jpg -------------------------------------------------------------------------------- /blog/404/index.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/80maker/vuepress-theme-maker/8f6f01812ebb0e424026e61a8fe077d34fedc174/blog/404/index.md -------------------------------------------------------------------------------- /blog/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | pageType: home 3 | --- -------------------------------------------------------------------------------- /blog/_post/maker.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2020-12-23 3 | category: maker 4 | cover: https://images.unsplash.com/photo-1560930375-f0f48057f535?fit=crop&w=1280&h=720&q=80 5 | --- 6 | # maker -------------------------------------------------------------------------------- /blog/_post/md5.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2020-11-26 3 | category: frontend 4 | title: 浏览器端不用第三方包也可以获取md5值 5 | --- 6 | 7 | > 利益于JS的同构特性,crypto虽然是Node.js的自带模块,但同时也能用于Web浏览器端。 8 | 9 | 10 | ## 双主角 11 | 12 | ### crypto 13 | 14 | ::: warning 15 | 这家伙是nodejs自带模块,测试了下在浏览器端也能正常使用 16 | ::: 17 | 18 | ### FileReader 19 | 20 | ## 隐藏的孪生兄弟 21 | 22 | ### atob和btoa 23 | 24 | ## 关键代码 25 | ``` js {5} 26 | // 将reader方法封装成Promisify 27 | async readFileData(fileReader, file) { 28 | return new Promise((resolve, reject) => { 29 | fileReader.addEventListener('load', (ev) => { 30 | resolve(ev.target.result); 31 | }); 32 | fileReader.readAsArrayBuffer(file); 33 | }); 34 | } 35 | 36 | // 代码调用 37 | const reader = new FileReader(); 38 | const crypto = require('crypto'); 39 | const md5 = crypto.createHash('md5'); 40 | let fileBuff = await that.readFileData(reader, file); 41 | fileBuff = Buffer.from(fileBuff); 42 | md5Str = md5.update(fileBuff).digest('hex'); 43 | ``` -------------------------------------------------------------------------------- /blog/_post/theme-learning-0.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2020-12-26 3 | title: 从零开始制作VuePress主题 4 | category: theme 5 | cover: /images/screenshot.webp 6 | tags: 7 | - VuePress 8 | --- 9 | ## 缘起 10 | 11 | 朋友早前注册的域名快到期了,有一定的纪念意义,丢弃了怪可惜的,所幸我用了份午饭钱给续了期。.com域名的年费似乎要贵上不少,也许是一份蛮丰盛的午餐,当然这是后话了。我自己是一名前端开发,技术栈是Vue.js.现在比较流行静态博客,不需要运行后台,恰好Vue官方推出了VuePress,不过VuePress生态的主题相对比较少,没找到即好看又适合自己的,所幸自己撸了一个(其实是从hexo圈子里移植过来的).因为这一份午餐,有了这个博客站点,也就有了这一份主题开发的记录整理。 12 | 13 | ## VuePress介绍 14 | 15 | 你现在看到的博客站点就是基于的这个主题。用的github pages部署,这是github提供的一项免费服务,我们可以在上边放些静态HTML页面组成一个站点。VuePress就是方便我们维护和生成这个静态站点的工具,类似的还有`Hugo`、`hexo`、`jekyll`等等 闻如其名,VuePress是由Vue.js驱动的,而且出自Vue官方团队之手。它的诞生初衷便是为了支持 Vue 及其子项目的文档需求,拥有完善的Markdown支持,并自带了一个专门为技术文档而优化的`默认主题`. 16 | 17 | ## VuePress背后的工作机制 18 | 事实上,一个开发阶段的VuePress站点是一个由Vue、Vue Router、Webpack共同驱动的单页应用,如果你熟悉用Vue-cli脚手架开发web前端应用,你会觉得这并无二样,甚至你仍然可以用Vue DevTools去调试你的自定义VuePress主题.在主题开发过程中这会给我们带来极大的方便--比如`计算属性`和`routes`。当构建发布站点时,VuePress会创建一个服务端渲染(ssr)版本,类似Nuxt,生成所有对应的HTML页面。 19 | 20 | ## 创建VuePress主题所需的工具和准备工作 21 | 22 | * [Node.js](https://nodejs.org/en/)>= 8.6 23 | * [Github Pages](https://pages.github.com/) 其它部署平台可参考 [官方部署文档](https://vuepress.vuejs.org/zh/guide/deploy.html) 24 | * 顺手的文本编辑器. e.g [VSCode](https://code.visualstudio.com/) 25 | 26 | ## 主题源码下载 27 | - 完整的主题源码都放在GitHub上了,可以随时Clone下来做为参照. 28 | 29 | - [`VuePress-theme-maker`](https://github.com/80maker) 30 | 31 | --- 32 | 33 | ## 开启VuePress主题开发之旅 34 | 35 | --- 36 | 37 | * :bear: [**常用术语**](/post/2021/01/01/theme-learning-concept.html) 38 | * :sheep: **主题目录结构** 39 | * :elephant: **模板文件和布局** 40 | * :koala: **主题继承和插件** 41 | * :hamster: **Header模板** 42 | * :eagle: **内置搜索** 43 | * :frog: **NavBar导航模板** 44 | * :whale: **SideBar模板** 45 | * :shark: **Archive文章归档页** 46 | * :cow2: **Category分类页** 47 | * :dragon_face: **Tag标签页** 48 | * :unicorn: **Post文章详情页** 49 | * :turtle: **Toc文章目录组件** 50 | * :snail: **FriendLink友情链接页** 51 | * :octopus: **主题插件开发--悬浮球菜单** 52 | * :tropical_fish: **响应式主题** 53 | * :bat: **主题暗黑模式** 54 | * :dolphin: **主题优化篇** -------------------------------------------------------------------------------- /blog/_post/theme-learning-concept.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2021-01-01 3 | title: VuePress中的常用术语 4 | category: theme 5 | tags: 6 | - VuePress 7 | --- -------------------------------------------------------------------------------- /blog/_post/theme-showcase.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2021-01-12 3 | title: Maker Theme Markdown 语法示例 4 | category: theme 5 | tags: 6 | - VuePress 7 | --- 8 | ## Heading (h2) 9 | 10 | ### h3 11 | 12 | #### h4 13 | 14 | ##### h5 15 | 16 | ###### h6 17 | 18 | ### [Heading link](/) 19 | 20 | ## 链接 21 | 22 | ### 内部链接 23 | 24 | 网站内部的链接,将会被转换成 `` 用于 SPA 导航。同时,站内的每一个文件夹下的 `README.md` 或者 `index.md` 文件都会被自动编译为 `index.html`,对应的链接将被视为 `/`。 25 | 26 | 以如下的文件结构为例: 27 | 28 | ``` 29 | . 30 | ├─ README.md 31 | ├─ foo 32 | │  ├─ README.md 33 | │ ├─ one.md 34 | │ └─ two.md 35 | └─ bar 36 | ├─ README.md 37 | ├─ three.md 38 | └─ four.md 39 | ``` 40 | 41 | 假设你现在在 `foo/one.md` 中: 42 | 43 | ``` md 44 | [Home](/) 45 | [foo](/foo/) 46 | [foo heading](./#heading) 47 | [bar - three](../bar/three.md) 48 | [bar - four](../bar/four.html) 49 | ``` 50 | 51 | ### 链接的重定向 52 | 53 | VuePress 支持重定向到干净链接。如果一个链接 `/foo` 找不到,VuePress 会自行寻找一个可用的 `/foo/` 或 `/foo.html`。反过来,当 `/foo/` 或 `/foo.html` 中的一个找不到时,VuePress 也会尝试寻找另一个。 54 | 55 | ::: tip 注意 56 | 无论是否使用了 permalink 和 clean-urls 插件,你的相对路径都应该依赖于当前的文件结构来定义。在上面的例子中,即使你将 `/foo/one.md` 的路径设为了 `/foo/one/`,你依然应该通过 `./two.md` 来访问 `/foo/two.md`。 57 | ::: 58 | 59 | ### 外部链接 60 | 61 | 外部的链接将会被自动地设置为 `target="_blank" rel="noopener noreferrer"`: 62 | 63 | - [vuejs.org](https://vuejs.org) 64 | - [VuePress on GitHub](https://github.com/vuejs/vuepress) 65 | 66 | 你可以自定义通过配置 [config.markdown.externalLinks](https://vuepress.vuejs.org/zh/config/#markdown-externallinks) 来自定义外部链接的特性。 67 | 68 | ## Front Matter 69 | 70 | Maker 提供了对 [YAML front matter](https://jekyllrb.com/docs/frontmatter/) 开箱即用的支持: 71 | 72 | ``` yaml 73 | --- 74 | title: Blogging Like a Hacker 75 | lang: en-US 76 | --- 77 | ``` 78 | 79 | 这些数据可以在当前 markdown 的正文,或者是任意的自定义或主题组件中使用。 80 | 81 | 想了解更多,请移步官方文档 [Front Matter](https://vuepress.vuejs.org/zh/guide/frontmatter.html#%E5%85%B6%E4%BB%96%E6%A0%BC%E5%BC%8F%E7%9A%84-front-matter)。 82 | 83 | ## Task List 84 | 85 | - [ ] Mercury 86 | - [x] Venus 87 | - [x] Earth (Orbit/Moon) 88 | - [x] Mars 89 | - [ ] Jupiter 90 | - [ ] Saturn 91 | - [ ] Uranus 92 | - [ ] Neptune 93 | - [ ] Comet Haley 94 | 95 | ## Image (click to zoom in/out) 96 | 97 | ``` markdown 98 | 99 | ![space](https://vuepress.vuejs.org/architecture.png) 100 | 101 | ``` 102 | 103 | ![](https://vuepress.vuejs.org/architecture.png) 104 | 105 | ## Inline image 106 | 107 | Not Bad.![](https://res.smzdm.com/images/emotions/138.png) 108 | 109 | ## hr 110 | 111 | Below is a `
`, I guess. 112 | 113 | --- 114 | 115 | Above is a `
`, I guess. 116 | 117 | ## List 118 | 119 | ### Unordered list 120 | 121 | + list item 122 | 123 | + list item 124 | 125 | - list item 126 | - list item 127 | 128 | + list item 129 | 130 | + list item 131 | 132 | ### Ordered list 133 | 134 | 1. list item 135 | 136 | 2. list item 137 | 138 | 3. list item 139 | ## GitHub 风格的表格 140 | 141 | **输入** 142 | 143 | ``` md 144 | | Tables | Are | Cool | 145 | | ------------- |:-------------:| -----:| 146 | | col 3 is | right-aligned | $1600 | 147 | | col 2 is | centered | $12 | 148 | | zebra stripes | are neat | $1 | 149 | ``` 150 | 151 | **输出** 152 | 153 | | Tables | Are | Cool | 154 | | ------------- |:-------------:| -----:| 155 | | col 3 is | right-aligned | $1600 | 156 | | col 2 is | centered | $12 | 157 | | zebra stripes | are neat | $1 | 158 | 159 | 160 | ## Long Table 161 | 162 | | Sun With Face | Grinning Face | Smiling Face | Grinning Face With Big Eyes | Smiling Face With Smiling Eyes | Full Moon Face | Grinning Face With Smiling Eyes | Face With Monocle | Cowboy Hat Face | Thinking Face | Face Vomiting | 163 | | ------------- |:-------------:| -----:| ------------- |:-------------:| -----:| ------------- |:-------------:| -----:|:-------------:| -----:| 164 | | 🌞 | 😀 | ☺️ | 😃 | 😊 | 🌝 | 😄 | 🧐 | 🤠 | 🤔 | 🤮 | 165 | | 🌞 | 😀 | ☺️ | 😃 | 😊 | 🌝 | 😄 | 🧐 | 🤠 | 🤔 | 🤮 | 166 | | 🌞 | 😀 | ☺️ | 😃 | 😊 | 🌝 | 😄 | 🧐 | 🤠 | 🤔 | 🤮 | 167 | 168 | ## Blockquote 169 | 170 | > 要么是酒,要么是女人,要么是神,家族,王,梦想,子女,力量,人如果不沉醉于某些东西估计都撑不下去吧,所有人都是某些东西的奴隶,就连那家伙... 171 | 172 | *-- 凯尼?阿克曼 《进击的巨人》* 173 | 174 | ## Code Block 175 | 176 | ``` js 177 | // 第 3 版规范的最终设计 178 | try { 179 | doSomething(); 180 | } catch (e) { 181 | if (e == "thing") 182 | console.log("a thing") 183 | else if (e == 42) 184 | console.log("42") 185 | else { 186 | console.log(e); 187 | throw e; // 重新 throw 188 | } 189 | } finally { 190 | cleanup(); 191 | } 192 | ``` 193 | 194 | ## Emoji 195 | 196 | **输入** 197 | 198 | ``` 199 | :tada: :100: 200 | ``` 201 | 202 | **输出** 203 | 204 | :tada: :100: 205 | 206 | 你可以在[这个列表](https://github.com/markdown-it/markdown-it-emoji/blob/master/lib/data/full.json)找到所有可用的 Emoji。 207 | 208 | ## 自定义容器 209 | 210 | **输入** 211 | 212 | ```md 213 | ::: tip 214 | 这是一个提示 215 | ::: 216 | 217 | ::: warning 218 | 这是一个警告 219 | ::: 220 | 221 | ::: danger 222 | 这是一个危险警告 223 | ::: 224 | 225 | ::: details 226 | 这是一个详情块,在 IE / Edge 中不生效 227 | ::: 228 | ``` 229 | 230 | **输出** 231 | 232 | ::: tip 233 | 这是一个提示 234 | ::: 235 | 236 | ::: warning 237 | 这是一个警告 238 | ::: 239 | 240 | ::: danger 241 | 这是一个危险警告 242 | ::: 243 | 244 | ::: details 245 | 这是一个详情块,在 IE / Edge 中不生效 246 | ::: 247 | 248 | 你也可以自定义块中的标题: 249 | 250 | ````md 251 | ::: danger STOP 252 | 危险区域,禁止通行 253 | ::: 254 | 255 | ::: details 点击查看代码 256 | ```js 257 | console.log('你好,VuePress!') 258 | ``` 259 | ::: 260 | ```` 261 | 262 | ::: danger STOP 263 | 危险区域,禁止通行 264 | ::: 265 | 266 | ::: details 点击查看代码 267 | ```js 268 | console.log('你好,VuePress!') 269 | ``` 270 | ::: 271 | 272 | **参考:** 273 | 274 | - [vuepress-plugin-container](https://vuepress.github.io/plugins/container/) 275 | 276 | ## 代码块中的语法高亮 277 | 278 | VuePress 使用了 [Prism](https://prismjs.com/) 来为 markdown 中的代码块实现语法高亮。Prism 支持大量的编程语言,你需要做的只是在代码块的开始倒勾中附加一个有效的语言别名: 279 | 280 | **输入** 281 | 282 | ```` 283 | ``` js 284 | export default { 285 | name: 'MyComponent', 286 | // ... 287 | } 288 | ``` 289 | ```` 290 | 291 | **输出** 292 | 293 | ``` js 294 | export default { 295 | name: 'MyComponent', 296 | // ... 297 | } 298 | ``` 299 | 300 | **输入** 301 | 302 | ```` 303 | ``` html 304 | 312 | ``` 313 | ```` 314 | 315 | **输出** 316 | 317 | ``` html 318 | 326 | ``` 327 | 328 | 在 Prism 的网站上查看 [合法的语言列表](https://prismjs.com/#languages-list)。 329 | 330 | 331 | ## 代码块中的行高亮 332 | 333 | **输入** 334 | 335 | ```` 336 | ``` js {4} 337 | export default { 338 | data () { 339 | return { 340 | msg: 'Highlighted!' 341 | } 342 | } 343 | } 344 | ``` 345 | ```` 346 | 347 | **输出** 348 | 349 | ``` js{4} 350 | export default { 351 | data () { 352 | return { 353 | msg: 'Highlighted!' 354 | } 355 | } 356 | } 357 | ``` 358 | 359 | 除了单行以外,你也可指定多行,行数区间,或是两者都指定。 360 | 361 | - 行数区间: 例如 `{5-8}`, `{3-10}`, `{10-17}` 362 | - 多个单行: 例如 `{4,7,9}` 363 | - 行数区间与多个单行: 例如 `{4,7-13,16,23-27,40}` 364 | 365 | **Input** 366 | 367 | ```` 368 | ``` js{1,4,6-7} 369 | export default { // Highlighted 370 | data () { 371 | return { 372 | msg: `Highlighted! 373 | This line isn't highlighted, 374 | but this and the next 2 are.`, 375 | motd: 'VuePress is awesome', 376 | lorem: 'ipsum', 377 | } 378 | } 379 | } 380 | ``` 381 | ```` 382 | 383 | **Output** 384 | 385 | ``` js{1,4,6-8} 386 | export default { // Highlighted 387 | data () { 388 | return { 389 | msg: `Highlighted! 390 | This line isn't highlighted, 391 | but this and the next 2 are.`, 392 | motd: 'VuePress is awesome', 393 | lorem: 'ipsum', 394 | } 395 | } 396 | } 397 | ``` 398 | 399 | ## 行号 400 | 401 | 你可以通过配置来为每个代码块显示行号: 402 | 403 | ``` js 404 | // .vuepress/config.js -> markdown 405 | 406 | module.exports = { 407 | markdown: { 408 | lineNumbers: true 409 | } 410 | } 411 | ``` 412 | 413 | ## 导入代码段 414 | 415 | 你可以通过下述的语法导入已经存在的文件中的代码段: 416 | 417 | ``` md 418 | <<< @/filepath 419 | ``` 420 | 421 | 它也支持 [行高亮](#代码块中的行高亮): 422 | 423 | ``` md 424 | <<< @/filepath{highlightLines} 425 | ``` 426 | 427 | **输入** 428 | 429 | ``` 430 | <<< @/blog/.vuepress/__tests__/snippet.js{2} 431 | ``` 432 | 433 | **输出** 434 | 435 | 436 | 437 | <<< @/blog/.vuepress/__tests__/snippet.js{2} 438 | 439 | 440 | 441 | ::: tip 注意 442 | 由于代码段的导入将在 webpack 编译之前执行,因此你无法使用 webpack 中的路径别名,此处的 `@` 默认值是 `process.cwd()`。 443 | ::: 444 | 445 | 446 | 为了只导入对应部分的代码,你也可运用 [VS Code region](https://code.visualstudio.com/docs/editor/codebasics#_folding)。你可以在文件路径后方的 `#` 紧接着提供一个自定义的区域名称(预设为 `snippet` ) 447 | 448 | **输入** 449 | 450 | ``` md 451 | <<< @/blog/.vuepress/__tests__/snippet-with-region.js#snippet{1} 452 | ``` 453 | 454 | **代码文件** 455 | 456 | 457 | 458 | <<< @/blog/.vuepress/__tests__/snippet-with-region.js 459 | 460 | 461 | 462 | **输出** 463 | 464 | 465 | 466 | <<< @/blog/.vuepress/__tests__/snippet-with-region.js#snippet{1} 467 | 468 | 469 | 470 | ## 进阶配置 471 | 472 | VuePress 使用 [markdown-it](https://github.com/markdown-it/markdown-it) 来渲染 Markdown,上述大多数的拓展也都是通过自定义的插件实现的。想要进一步的话,你可以通过 `.vuepress/config.js` 的 `markdown` 选项,来对当前的 `markdown-it` 实例做一些自定义的配置: 473 | 474 | ``` js 475 | module.exports = { 476 | markdown: { 477 | // markdown-it-anchor 的选项 478 | anchor: { permalink: false }, 479 | // markdown-it-toc 的选项 480 | toc: { includeLevel: [1, 2] }, 481 | extendMarkdown: md => { 482 |      // 使用更多的 markdown-it 插件! 483 | md.use(require('markdown-it-xxx')) 484 | } 485 | } 486 | } 487 | ``` 488 | 489 | ## mark 490 | 491 | ==Marked text== 492 | 493 | ### Footnotes 494 | 495 | Footnote 1 link[^first]. 496 | 497 | Footnote 2 link[^second]. 498 | 499 | Inline footnote^[Text of inline footnote] definition. 500 | 501 | Duplicated footnote reference[^second]. 502 | 503 | [^first]: Footnote **can have markup** 504 | 505 | and multiple paragraphs. 506 | 507 | [^second]: Footnote text. 508 | 509 | ### Abbreviations 510 | 511 | This is HTML abbreviation example. 512 | 513 | It converts "HTML", but keep intact partial entries like "xxxHTMLyyy" and so on. 514 | 515 | *[HTML]: Hyper Text Markup Language 516 | 517 | -------------------------------------------------------------------------------- /blog/friend-links/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: It is always a pleasure to greet a friend from afar ~ 3 | cover: /images/friend.jpg 4 | links: 5 | - title: Ikeq Cheng 6 | roundness: true 7 | logo: https://www.gravatar.com/avatar/06818ca4361a28447cffdd2daaed2799?s=160 8 | url: https://blog.oniuo.com/ 9 | desc: The whole problem with the world is that fools and fanatics are always so certain of themselves, but wiser people so full of doubts. 10 | - title: 17ria 11 | logo: https://www.17ria.com/themes/fruit/assets/images/safari-pinned-tab.svg 12 | url: https://www.17ria.com/ 13 | desc: 承载最初的简单梦想,记录前端路上的点点滴滴~ 14 | --- 15 | 16 | ### 友链申请 -------------------------------------------------------------------------------- /components/Archive.vue: -------------------------------------------------------------------------------- 1 | 21 | 62 | -------------------------------------------------------------------------------- /components/Category.vue: -------------------------------------------------------------------------------- 1 | 9 | 14 | -------------------------------------------------------------------------------- /components/CategoryItem.vue: -------------------------------------------------------------------------------- 1 | 15 | 40 | -------------------------------------------------------------------------------- /components/ColorScheme.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | -------------------------------------------------------------------------------- /components/Comments.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | -------------------------------------------------------------------------------- /components/DarkMode.vue: -------------------------------------------------------------------------------- 1 | 8 | 53 | -------------------------------------------------------------------------------- /components/DropdownLink.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 112 | 113 | -------------------------------------------------------------------------------- /components/DropdownTransition.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | 27 | -------------------------------------------------------------------------------- /components/FooterBar.vue: -------------------------------------------------------------------------------- 1 | 22 | 27 | -------------------------------------------------------------------------------- /components/FriendLink.vue: -------------------------------------------------------------------------------- 1 | 28 | 46 | -------------------------------------------------------------------------------- /components/Home.vue: -------------------------------------------------------------------------------- 1 | 23 | 44 | -------------------------------------------------------------------------------- /components/NavLink.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | -------------------------------------------------------------------------------- /components/NavLinks.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 115 | 116 | -------------------------------------------------------------------------------- /components/Navbar.vue: -------------------------------------------------------------------------------- 1 | 7 | 18 | -------------------------------------------------------------------------------- /components/Post.vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 99 | 100 | -------------------------------------------------------------------------------- /components/PostMeta.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 99 | 100 | 131 | -------------------------------------------------------------------------------- /components/PostNav.vue: -------------------------------------------------------------------------------- 1 | 7 | 14 | -------------------------------------------------------------------------------- /components/PostTag.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 24 | 25 | -------------------------------------------------------------------------------- /components/Reward.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 64 | 65 | 133 | -------------------------------------------------------------------------------- /components/SettingPanel.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /components/SideBar.vue: -------------------------------------------------------------------------------- 1 | 26 | 41 | -------------------------------------------------------------------------------- /components/Sticker.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 67 | 68 | -------------------------------------------------------------------------------- /components/SubNav.vue: -------------------------------------------------------------------------------- 1 | 17 | 30 | -------------------------------------------------------------------------------- /components/SvgSprite.vue: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /components/Tag.vue: -------------------------------------------------------------------------------- 1 | 9 | 14 | -------------------------------------------------------------------------------- /components/TagItem.vue: -------------------------------------------------------------------------------- 1 | 13 | 38 | -------------------------------------------------------------------------------- /components/Toc.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 125 | -------------------------------------------------------------------------------- /components/Valine.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 45 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 确保脚本抛出遇到的错误 4 | set -e 5 | 6 | # 生成静态文件 7 | npm run blog:build 8 | 9 | # 进入生成的文件夹 10 | cd blog/.vuepress/dist 11 | 12 | # 如果是发布到自定义域名 13 | echo '80shuo.com' > CNAME 14 | 15 | git init 16 | git add -A 17 | git commit -m 'deploy' 18 | 19 | # 如果发布到 https://.github.io 20 | # git push -f git@github.com:/.github.io.git master 21 | 22 | # 如果发布到 https://.github.io/ 23 | git push -f git@github.com:80maker/80maker.github.io.git master:gh-pages 24 | 25 | cd - -------------------------------------------------------------------------------- /global-components/Badge.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | -------------------------------------------------------------------------------- /global-components/Icon.vue: -------------------------------------------------------------------------------- 1 | 4 | 14 | -------------------------------------------------------------------------------- /global-components/RelatedPosts.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /global-components/ThemeSWUpdatePopup.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | 20 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | module.exports = (options, {themeConfig}) => { 3 | /** 4 | * Default theme configuration 5 | */ 6 | themeConfig = Object.assign(themeConfig, { 7 | searchPlaceholder: themeConfig.searchPlaceholder || 'Search', 8 | nav: themeConfig.nav || [ 9 | { text: '🏠 Home', link: '/' } 10 | ], 11 | hostname: themeConfig.hostname || '', 12 | wordPerminute: themeConfig.wordPerminute || {cn: 300, en: 160}, 13 | social: themeConfig.social || [], 14 | reward: themeConfig.reward || {}, 15 | dateFormat: themeConfig.dateFormat || 'MMM DD, YYYY' 16 | }) 17 | 18 | const plugins = [ 19 | ['@vuepress/nprogress'], 20 | ['@vuepress/search'], 21 | ['flowchart'], 22 | ['vuepress-plugin-container', { 23 | type: 'tip', 24 | defaultTitle: { 25 | '/': 'TIP', 26 | '/zh/': '提示' 27 | } 28 | }], 29 | ['vuepress-plugin-container', { 30 | type: 'warning', 31 | defaultTitle: { 32 | '/': 'WARNING', 33 | '/zh/': '注意' 34 | } 35 | }], 36 | ['vuepress-plugin-container', { 37 | type: 'danger', 38 | defaultTitle: { 39 | '/': 'WARNING', 40 | '/zh/': '警告' 41 | } 42 | }], 43 | ['vuepress-plugin-container', { 44 | type: 'details', 45 | before: info => `
${info ? `${info}` : ''}\n`, 46 | after: () => '
\n' 47 | }], 48 | ['vuepress-plugin-container', { 49 | type: 'demo', 50 | before: () => `\n`, 51 | after: () => '\n' 52 | }], 53 | ['@vuepress/medium-zoom', { 54 | selector: '.article-content img', 55 | // medium-zoom options here 56 | // See: https://github.com/francoischalifour/medium-zoom#options 57 | options: { 58 | margin: 16, 59 | background: "#FF0000", 60 | } 61 | }], 62 | ['@vuepress/blog', themeConfig.blog || { 63 | directories: [ 64 | { 65 | id: 'post', 66 | dirname: '_post', 67 | path: '/', 68 | itemPermalink: '/post/:year/:month/:day/:slug.html', 69 | pagination: { 70 | perPagePosts: 10, 71 | prevText: '', 72 | nextText: '' 73 | } 74 | } 75 | ], 76 | frontmatters: [ 77 | { 78 | id: "tag", 79 | keys: ['tag', 'tags'], 80 | path: '/tags/', 81 | frontmatter: { title: 'Tag' }, 82 | pagination: { 83 | lengthPerPage: 10, 84 | prevText: '', 85 | nextText: '' 86 | } 87 | }, 88 | { 89 | id: "category", 90 | keys: ['category', 'categories'], 91 | path: '/categories/', 92 | frontmatter: { title: 'Category' }, 93 | pagination: { 94 | lengthPerPage: 10, 95 | prevText: '', 96 | nextText: '' 97 | } 98 | } 99 | ] 100 | }], 101 | [ 102 | 'vuepress-plugin-seo', themeConfig.seo || false 103 | ], 104 | ['@vuepress/pwa', themeConfig.pwa || false], 105 | ['one-click-copy', themeConfig.copy || false], 106 | require('./plugin/demo-code'), 107 | require('./plugin/theme-utils'), 108 | require('./plugin/float-menu') 109 | ]; 110 | if(themeConfig.palette) { 111 | plugins.push(require('./plugin/theme-palette')); 112 | } 113 | const config = { 114 | plugins, 115 | alias: { 116 | assets: path.resolve(__dirname, 'assets'), 117 | } 118 | } 119 | 120 | return config 121 | } -------------------------------------------------------------------------------- /layouts/404.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 38 | -------------------------------------------------------------------------------- /layouts/Layout.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 135 | -------------------------------------------------------------------------------- /mixins/index.js: -------------------------------------------------------------------------------- 1 | export default { 2 | computed: { 3 | /** 4 | * @description 【页面实例属性】下一篇文章 5 | * @returns { Post } 6 | */ 7 | $mkNextPageItem() { 8 | let list = this.getArticleList(); 9 | const index = list.findIndex(item => { 10 | return item.path === this.$page.path; 11 | }) 12 | if (index === -1) { 13 | return; 14 | } 15 | return list[index + 1]; 16 | }, 17 | /** 18 | * @description 【页面实例属性】上一篇文章 19 | * @returns { Post } 20 | */ 21 | $mkPrevPageItem() { 22 | let list = this.getArticleList(); 23 | const index = list.findIndex(item => { 24 | return item.path === this.$page.path; 25 | }) 26 | if (index === -1) { 27 | return; 28 | } 29 | return list[index - 1]; 30 | } 31 | }, 32 | methods: { 33 | /** 34 | * @description 【页面实例方法】根据pid获取文章列表 35 | * @param { string } pid 36 | * @returns { article list } 37 | */ 38 | getArticleList() { 39 | const pid = this.$page.pid; 40 | let list = this.$site.pages.filter(item => { 41 | return item.pid === pid; 42 | }); 43 | list = list.sort((a,b) => { 44 | let time1 = new Date(a.frontmatter.date); 45 | let time2 = new Date(b.frontmatter.date); 46 | return time1 - time2; 47 | }) 48 | return list; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vuepress-theme-maker", 3 | "version": "1.2.3", 4 | "description": "🐉 A flat and clean Blog Theme for VuePress site | 一款多配置、简约风的VuePress主题.", 5 | "keywords": [ 6 | "vuepress-theme-blog", 7 | "vuepress-theme", 8 | "blog-theme", 9 | "vue", 10 | "vuepress" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "80maker/vuepress-theme-maker" 15 | }, 16 | "main": "index.js", 17 | "scripts": { 18 | "blog:dev": "vuepress dev blog", 19 | "blog:build": "vuepress build blog", 20 | "deploy": "sh deploy.sh" 21 | }, 22 | "author": "80maker", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/80maker/vuepress-theme-maker/issues" 26 | }, 27 | "homepage": "https://80shuo.com/", 28 | "dependencies": { 29 | "@vuepress/plugin-blog": "^1.9.3", 30 | "@vuepress/plugin-medium-zoom": "^1.8.0", 31 | "@vuepress/plugin-pwa": "^1.8.0", 32 | "markdown-it-abbr": "^1.0.4", 33 | "markdown-it-footnote": "^3.0.2", 34 | "markdown-it-mark": "^3.0.1", 35 | "markdown-it-task-lists": "^2.1.1", 36 | "vuepress": "^1.7.1", 37 | "vuepress-plugin-flowchart": "^1.4.3", 38 | "vuepress-plugin-seo": "^0.1.4", 39 | "vuepress-plugin-one-click-copy": "^1.0.2", 40 | "valine": "^1.4.14" 41 | }, 42 | "publishConfig": { 43 | "registry": "https://registry.npmjs.org/" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /plugin/demo-code/DemoCode.vue: -------------------------------------------------------------------------------- 1 | 17 | 34 | -------------------------------------------------------------------------------- /plugin/demo-code/enhanceAppFile.js: -------------------------------------------------------------------------------- 1 | 2 | import DemoCode from './DemoCode.vue' 3 | 4 | export default ({ Vue }) => { 5 | Vue.component('DemoCode', DemoCode) 6 | } -------------------------------------------------------------------------------- /plugin/demo-code/index.js: -------------------------------------------------------------------------------- 1 | const { path } = require('@vuepress/shared-utils') 2 | 3 | module.exports = { 4 | name: 'maker-code', 5 | enhanceAppFiles: [ 6 | path.resolve(__dirname, 'enhanceAppFile.js') 7 | ] 8 | } -------------------------------------------------------------------------------- /plugin/float-menu/FloatMenu.vue: -------------------------------------------------------------------------------- 1 | 32 | 96 | -------------------------------------------------------------------------------- /plugin/float-menu/Search.vue: -------------------------------------------------------------------------------- 1 | 9 | 46 | -------------------------------------------------------------------------------- /plugin/float-menu/enhanceAppFile.js: -------------------------------------------------------------------------------- 1 | 2 | import FloatMenu from './FloatMenu.vue' 3 | import Search from './Search.vue' 4 | 5 | export default ({ Vue }) => { 6 | // eslint-disable-next-line vue/match-component-file-name 7 | Vue.component('FloatMenu', FloatMenu) 8 | Vue.component('Search', Search) 9 | Vue.prototype.$eventBus = new Vue(); 10 | } -------------------------------------------------------------------------------- /plugin/float-menu/index.js: -------------------------------------------------------------------------------- 1 | const { path } = require('@vuepress/shared-utils') 2 | 3 | module.exports = { 4 | name: 'maker-float-menu', 5 | enhanceAppFiles: [ 6 | path.resolve(__dirname, 'enhanceAppFile.js') 7 | ], 8 | 9 | globalUIComponents: ['FloatMenu', 'Search'] 10 | } -------------------------------------------------------------------------------- /plugin/theme-palette/ThemePalette.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 42 | 43 | -------------------------------------------------------------------------------- /plugin/theme-palette/enhanceAppFile.js: -------------------------------------------------------------------------------- 1 | 2 | import ThemePalette from './ThemePalette.vue' 3 | 4 | export default ({ Vue }) => { 5 | Vue.component('ThemePalette', ThemePalette) 6 | } -------------------------------------------------------------------------------- /plugin/theme-palette/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (options = {}, context) => ({ 2 | name: 'maker-theme-palette', 3 | enhanceAppFiles: [ 4 | path.resolve(__dirname, 'enhanceAppFile.js') 5 | ] 6 | }) -------------------------------------------------------------------------------- /plugin/theme-utils/index.js: -------------------------------------------------------------------------------- 1 | const counter = function (content) { 2 | const cn = (content.match(/[\u4E00-\u9FA5]/g) || []).length; 3 | const en = (content.replace(/[\u4E00-\u9FA5]/g, '').match(/[a-zA-Z0-9_\u0392-\u03c9\u0400-\u04FF]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af\u0400-\u04FF]+|[\u00E4\u00C4\u00E5\u00C5\u00F6\u00D6]+|\w+/g) || []).length; 4 | return [cn, en]; 5 | }; 6 | const calcReadingTime = function(content, { cn = 300, en = 160 } = {}) { 7 | var len = counter(content); 8 | var readingTime = len[0] / cn + len[1] / en; 9 | return readingTime < 1 ? '1' : parseInt(readingTime, 10); 10 | } 11 | const wordcount = function (content) { 12 | var len = counter(content); 13 | var count = len[0] + len[1]; 14 | if (count < 1000) { 15 | return count; 16 | } 17 | return Math.round(count / 100) / 10 + 'k'; 18 | } 19 | 20 | module.exports = (options = {}, context) => ({ 21 | name: 'maker-theme-utils', 22 | extendPageData($page) { 23 | if ($page.path === '/archives/') { 24 | return $page.pageType = 'archive'; 25 | } else if ($page.path === '/categories/') { 26 | return $page.pageType = 'category'; 27 | } else if (/^\/categories\/\w/.test($page.path)) { 28 | return $page.pageType = 'categoryItem'; 29 | } else if ($page.path === '/tags/') { 30 | return $page.pageType = 'tag'; 31 | } else if (/^\/tags\/\w/.test($page.path)) { 32 | return $page.pageType = 'tagItem'; 33 | } else if ($page.path === '/' || $page.path.startsWith('/page/')) { 34 | return $page.pageType = 'home'; 35 | } else if ($page.path === '/friend-links/') { 36 | return $page.pageType = 'friendLink'; 37 | } 38 | if ($page.pid === 'post') { 39 | const { _strippedContent } = $page; 40 | let content = _strippedContent.replace(/\s/g, ''); 41 | $page.wordCount = wordcount(content); 42 | $page.readingTime = calcReadingTime(content, context.themeConfig.wordPerminute); 43 | } 44 | }, 45 | additionalPages() { 46 | const pages = [{ 47 | path: '/archives/', 48 | frontmatter: { 49 | title: 'Archive' 50 | } 51 | }]; 52 | return pages; 53 | } 54 | }) -------------------------------------------------------------------------------- /styles/code.styl: -------------------------------------------------------------------------------- 1 | {$contentClass}, {$descClass}, .friend-link__content 2 | blockquote 3 | background: var(--theme-accent-color-005); 4 | border-left: 3px solid #ccc; 5 | border-radius 3px 6 | margin: 1.5em 0; 7 | padding: .5em 10px 1em; 8 | quotes: "\201C""\201D""\2018""\2019"; 9 | &:before 10 | color: #ccc; 11 | content: open-quote; 12 | font-size: 4em; 13 | line-height: .1em; 14 | margin-right: .25em; 15 | vertical-align: -.4em; 16 | table 17 | max-width: 100%; 18 | display: block; 19 | border-collapse collapse 20 | margin: 1rem 0; 21 | overflow-x: auto 22 | 23 | 24 | tr 25 | border-top: 1px solid #dfe2e5 26 | 27 | 28 | tr:nth-child(2n) 29 | background-color: var(--theme-bg-tertiary-color) 30 | 31 | th 32 | white-space: nowrap; 33 | font-weight: 700; 34 | td,th 35 | border: 1px solid #dfe2e5; 36 | padding: .6em 1em 37 | pre, pre[class*="language-"] 38 | border 1px solid var(--theme-border-color) 39 | line-height 1.5 40 | padding 1.25rem 1.5rem 41 | margin 0.85rem 0 42 | border-radius 6px 43 | overflow auto 44 | code 45 | padding 0 46 | background-color transparent 47 | border-radius 0 48 | &.copy-code-enabled div[class*="language-"]:hover 49 | &::before 50 | opacity: 0 51 | .code-copy 52 | opacity: 1 53 | div[class*="language-"] 54 | position relative 55 | border-radius 6px 56 | .highlight-lines 57 | user-select none 58 | padding-top 1.3rem 59 | position absolute 60 | top 0 61 | left 0 62 | width 100% 63 | line-height 1.5 64 | .highlighted 65 | opacity: .2 66 | background-color var(--theme-highlight-02) 67 | pre, pre[class*="language-"] 68 | background var(--theme-accent-color-005) 69 | position relative 70 | z-index 1 71 | .code-copy 72 | right: .8em 73 | &::before 74 | position absolute 75 | z-index 3 76 | top 0.8em 77 | right 1em 78 | font-size 0.75rem 79 | color var(--theme-highlight-03-085) 80 | &:not(.line-numbers-mode) 81 | .line-numbers-wrapper 82 | display none 83 | &.line-numbers-mode 84 | .highlight-lines .highlighted 85 | position relative 86 | &:before 87 | content ' ' 88 | position absolute 89 | z-index 3 90 | left 0 91 | top 0 92 | display block 93 | width $lineNumbersWrapperWidth 94 | height 100% 95 | opacity: .2 96 | background-color var(--theme-highlight-02) 97 | pre 98 | padding-left $lineNumbersWrapperWidth + 1 rem 99 | vertical-align middle 100 | .line-numbers-wrapper 101 | position absolute 102 | top 0 103 | width $lineNumbersWrapperWidth 104 | text-align center 105 | color var(--theme-highlight-03-085) 106 | padding 1.25rem 0 107 | line-height 1.5 108 | br 109 | user-select none 110 | .line-number 111 | position relative 112 | z-index 4 113 | user-select none 114 | font-size 0.85em 115 | &::after 116 | content '' 117 | position absolute 118 | z-index 2 119 | top 0 120 | left 0 121 | width $lineNumbersWrapperWidth 122 | height 100% 123 | border-radius 6px 0 0 6px 124 | border-right 1px solid var(--theme-highlight-02-048) 125 | background: var(--theme-highlight-01-048) 126 | 127 | 128 | for lang in $codeLang 129 | div{'[class~="language-' + lang + '"]'} 130 | &:before 131 | content ('' + lang) 132 | 133 | div[class~="language-javascript"] 134 | &:before 135 | content "js" 136 | 137 | div[class~="language-typescript"] 138 | &:before 139 | content "ts" 140 | 141 | div[class~="language-markup"] 142 | &:before 143 | content "html" 144 | 145 | div[class~="language-markdown"] 146 | &:before 147 | content "md" 148 | 149 | div[class~="language-json"]:before 150 | content "json" 151 | 152 | div[class~="language-ruby"]:before 153 | content "rb" 154 | 155 | div[class~="language-python"]:before 156 | content "py" 157 | 158 | div[class~="language-bash"]:before 159 | content "sh" 160 | 161 | div[class~="language-php"]:before 162 | content "php" 163 | 164 | code 165 | color: var(--theme-highlight-04); 166 | padding: .25rem .5rem; 167 | margin: 0; 168 | font-size: .85em; 169 | background-color: var(--theme-accent-color-005); 170 | border-radius: 3px; 171 | 172 | // ========== mode =========== 173 | code[class*="language-"], 174 | pre[class*="language-"] { 175 | color: var(--theme-highlight-04); 176 | background: none; 177 | font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; 178 | text-align: left; 179 | white-space: pre; 180 | word-spacing: normal; 181 | word-break: normal; 182 | word-wrap: normal; 183 | line-height: 1.5; 184 | -moz-tab-size: 4; 185 | -o-tab-size: 4; 186 | tab-size: 4; 187 | -webkit-hyphens: none; 188 | -moz-hyphens: none; 189 | -ms-hyphens: none; 190 | hyphens: none; 191 | } 192 | 193 | pre[class*="language-"]::-moz-selection, 194 | pre[class*="language-"] ::-moz-selection, 195 | code[class*="language-"]::-moz-selection, 196 | code[class*="language-"] ::-moz-selection { 197 | background: var(--theme-highlight-02); 198 | } 199 | 200 | pre[class*="language-"]::selection, 201 | pre[class*="language-"] ::selection, 202 | code[class*="language-"]::selection, 203 | code[class*="language-"] ::selection { 204 | background: var(--theme-highlight-02); 205 | } 206 | 207 | /* Code blocks */ 208 | pre[class*="language-"] { 209 | padding: 1em; 210 | margin: 0.5em 0; 211 | overflow: auto; 212 | } 213 | 214 | :not(pre) > code[class*="language-"], 215 | pre[class*="language-"] { 216 | background: var(--theme-highlight-00); 217 | } 218 | 219 | /* Inline code */ 220 | :not(pre) > code[class*="language-"] { 221 | padding: 0.1em 0.3em; 222 | border-radius: 0.3em; 223 | white-space: normal; 224 | } 225 | 226 | .token.comment, 227 | .token.prolog, 228 | .token.doctype, 229 | .token.cdata { 230 | color: var(--theme-highlight-03); 231 | } 232 | 233 | .token.punctuation { 234 | color: var(--theme-highlight-04); 235 | } 236 | 237 | .token.delimiter.important, 238 | .token.selector .parent, 239 | .token.tag, 240 | .token.tag .token.punctuation { 241 | color: var(--theme-highlight-05); 242 | } 243 | 244 | .token.attr-name, 245 | .token.boolean, 246 | .token.boolean.important, 247 | .token.number, 248 | .token.constant, 249 | .token.selector .token.attribute { 250 | color: var(--theme-highlight-06); 251 | } 252 | 253 | .token.class-name, 254 | .token.key, 255 | .token.parameter, 256 | .token.property, 257 | .token.property-access, 258 | .token.variable { 259 | color: var(--theme-highlight-07); 260 | } 261 | 262 | .token.attr-value, 263 | .token.inserted, 264 | .token.color, 265 | .token.selector .token.value, 266 | .token.string, 267 | .token.string .token.url-link { 268 | color: var(--theme-highlight-08); 269 | } 270 | 271 | .token.builtin, 272 | .token.keyword-array, 273 | .token.package, 274 | .token.regex { 275 | color: var(--theme-highlight-09); 276 | } 277 | 278 | .token.function, 279 | .token.selector .token.class, 280 | .token.selector .token.id { 281 | color: var(--theme-highlight-0a); 282 | } 283 | 284 | .token.atrule .token.rule, 285 | .token.combinator, 286 | .token.keyword, 287 | .token.operator, 288 | .token.pseudo-class, 289 | .token.pseudo-element, 290 | .token.selector, 291 | .token.unit { 292 | color: var(--theme-highlight-0b); 293 | } 294 | 295 | .token.deleted, 296 | .token.important { 297 | color: var(--theme-highlight-0c); 298 | } 299 | 300 | .token.keyword-this, 301 | .token.this { 302 | color: var(--theme-highlight-07); 303 | } 304 | 305 | .token.important, 306 | .token.keyword-this, 307 | .token.this, 308 | .token.bold { 309 | font-weight: bold; 310 | } 311 | 312 | .token.delimiter.important { 313 | font-weight: inherit; 314 | } 315 | 316 | .token.italic { 317 | font-style: italic; 318 | } 319 | 320 | .token.entity { 321 | cursor: help; 322 | } 323 | 324 | .language-markdown .token.title, 325 | .language-markdown .token.title .token.punctuation { 326 | color: var(--theme-highlight-07); 327 | font-weight: bold; 328 | } 329 | 330 | .language-markdown .token.blockquote.punctuation { 331 | color: var(--theme-highlight-09); 332 | } 333 | 334 | .language-markdown .token.code { 335 | color: var(--theme-highlight-05); 336 | } 337 | 338 | .language-markdown .token.hr.punctuation { 339 | color: var(--theme-highlight-07); 340 | } 341 | 342 | .language-markdown .token.url > .token.content { 343 | color: var(--theme-highlight-08); 344 | } 345 | 346 | .language-markdown .token.url-link { 347 | color: var(--theme-highlight-06); 348 | } 349 | 350 | .language-markdown .token.list.punctuation { 351 | color: var(--theme-highlight-09); 352 | } 353 | 354 | .language-markdown .token.table-header { 355 | color: var(--theme-highlight-04); 356 | } 357 | 358 | .language-json .token.operator { 359 | color: var(--theme-highlight-04); 360 | } 361 | 362 | .language-scss .token.variable { 363 | color: var(--theme-highlight-05); 364 | } 365 | 366 | /* overrides color-values for the Show Invisibles plugin 367 | * https://prismjs.com/plugins/show-invisibles/ 368 | */ 369 | .token.tab:not(:empty):before, 370 | .token.cr:before, 371 | .token.lf:before, 372 | .token.space:before { 373 | color: var(--theme-highlight-03); 374 | } 375 | 376 | /* overrides color-values for the Match Braces plugin 377 | * https://prismjs.com/plugins/match-braces/ 378 | */ 379 | .rainbow-braces .token.punctuation.brace-level-1, 380 | .rainbow-braces .token.punctuation.brace-level-5, 381 | .rainbow-braces .token.punctuation.brace-level-9 { 382 | color: var(--theme-highlight-06); 383 | } 384 | 385 | .rainbow-braces .token.punctuation.brace-level-2, 386 | .rainbow-braces .token.punctuation.brace-level-6, 387 | .rainbow-braces .token.punctuation.brace-level-10 { 388 | color: var(--theme-highlight-09); 389 | } 390 | 391 | .rainbow-braces .token.punctuation.brace-level-3, 392 | .rainbow-braces .token.punctuation.brace-level-7, 393 | .rainbow-braces .token.punctuation.brace-level-11 { 394 | color: var(--theme-highlight-07); 395 | } 396 | 397 | .rainbow-braces .token.punctuation.brace-level-4, 398 | .rainbow-braces .token.punctuation.brace-level-8, 399 | .rainbow-braces .token.punctuation.brace-level-12 { 400 | color: var(--theme-highlight-0a); 401 | } 402 | 403 | 404 | // 流程图 405 | .vuepress-flowchart 406 | overflow auto 407 | 408 | // hr 409 | hr 410 | margin: 2.5em auto; 411 | width: 4px; 412 | height: 4px; 413 | border: 0; 414 | border-radius: 3px; 415 | background-color: currentColor; 416 | box-shadow: 1em 0 0 0 currentColor,-1em 0 0 0 currentColor -------------------------------------------------------------------------------- /styles/color_scheme.styl: -------------------------------------------------------------------------------- 1 | $lightMode = 2 | --theme-background #ffffff url('../assets/images/bg.png') 3 | --theme-accent-color $accentColor 4 | --theme-foreground-color: $textColor; 5 | --theme-border-color: $borderColor; 6 | --theme-sidebar-background: $accentColor linear-gradient(to bottom, $accentColor 0%, darken($accentColor, 20%) 100%); 7 | --theme-card-background: #fff; 8 | --theme-highlight-00: #e3eaf2; 9 | --theme-highlight-01: #d0dae7; 10 | --theme-highlight-01-048: alpha(#d0dae7, .48); 11 | --theme-highlight-02: #8da1b9; 12 | --theme-highlight-02-048: alpha(#8da1b9, .48); 13 | --theme-highlight-03: #3c526d; 14 | --theme-highlight-03-085: alpha(#3c526d, .85); 15 | --theme-highlight-04: #111b27; 16 | --theme-highlight-05: #006d6d; 17 | --theme-highlight-06: #755f00; 18 | --theme-highlight-07: #005a8e; 19 | --theme-highlight-08: #116b00; 20 | --theme-highlight-09: #af00af; 21 | --theme-highlight-0a: #7c00aa; 22 | --theme-highlight-0b: #a04900; 23 | --theme-highlight-0c: #c22f2e; 24 | --theme-bg-tertiary-color #f6f8fa 25 | --theme-accent-color-005: alpha($accentColor, .05) 26 | --theme-accent-color-01: alpha($accentColor, .1) 27 | --theme-accent-color-02: alpha($accentColor, .2) 28 | --theme-accent-color-04: alpha($accentColor, .4) 29 | --theme-accent-color-08: alpha($accentColor, .8) 30 | 31 | $darkMode = 32 | --theme-accent-color: $dark_accentColor; 33 | --theme-foreground-color: $dark_textColor; 34 | --theme-border-color: $dark_borderColor; 35 | --theme-background: #202020; 36 | --theme-sidebar-background: $dark_accentColor; 37 | --theme-card-background: $dark_cardBackgroundColor; 38 | --theme-highlight-00: #111b27; 39 | --theme-highlight-01: #213043; 40 | --theme-highlight-01-048: alpha(#213043, .48); 41 | --theme-highlight-02: #3c526d; 42 | --theme-highlight-02-048: alpha(#3c526d, .48); 43 | --theme-highlight-03: #8da1b9; 44 | --theme-highlight-03-085: alpha(#8da1b9, .85); 45 | --theme-highlight-04: #e3eaf2; 46 | --theme-highlight-05: #66cccc; 47 | --theme-highlight-06: #e6d37a; 48 | --theme-highlight-07: #6cb8e6; 49 | --theme-highlight-08: #91d076; 50 | --theme-highlight-09: #f4adf4; 51 | --theme-highlight-0a: #c699e3; 52 | --theme-highlight-0b: #e9ae7e; 53 | --theme-highlight-0c: #cd6660; 54 | --theme-card-color: #252525; 55 | --theme-bg-tertiary-color #161b22 56 | --theme-accent-color-005: alpha($dark_accentColor, .05) 57 | --theme-accent-color-01: alpha($dark_accentColor, .1) 58 | --theme-accent-color-02: alpha($dark_accentColor, .2) 59 | --theme-accent-color-04: alpha($dark_accentColor, .4) 60 | --theme-accent-color-08: alpha($dark_accentColor, .8) 61 | 62 | html 63 | --theme-content-width: 660px; 64 | --theme-font-base: 'Josefin Sans','PingFang SC','Microsoft YaHei'; 65 | --theme-font-logo: Lobster,cursive,'Josefin Sans','PingFang SC','Microsoft YaHei'; 66 | --theme-font-menu: IM Fell DW Pica SC,'Josefin Sans','PingFang SC','Microsoft YaHei'; 67 | --theme-font-heading: impact,'Josefin Sans','PingFang SC','Microsoft YaHei'; 68 | --theme-font-label: Sarpanch,'Josefin Sans','PingFang SC','Microsoft YaHei'; 69 | --theme-font-print: 'PingFang SC','Microsoft YaHei'; 70 | {$lightMode} 71 | 72 | @media (prefers-color-scheme: dark) 73 | html 74 | {$darkMode} 75 | .float-menu:before 76 | box-shadow none 77 | 78 | html.light 79 | {$lightMode} 80 | 81 | html.dark 82 | {$darkMode} 83 | .float-menu:before 84 | box-shadow none -------------------------------------------------------------------------------- /styles/custom_block.styl: -------------------------------------------------------------------------------- 1 | .custom-block 2 | .custom-block-title 3 | font-weight 600 4 | margin-bottom -0.4rem 5 | &.tip, &.warning, &.danger 6 | padding .1rem 1.5rem 7 | border-left-width 3px 8 | border-left-style solid 9 | margin 1rem 0 10 | border-radius: 3px; 11 | word-break: break-word; 12 | line-height: 1.8; 13 | background-color var(--theme-accent-color-005); 14 | &.tip 15 | border-color var(--theme-accent-color) 16 | &.warning 17 | border-color darken(#ffe564, 35%) 18 | color var(--theme-foreground-color) 19 | .custom-block-title 20 | color darken(#ffe564, 50%) 21 | a 22 | color var(--theme-foreground-color) 23 | &.danger 24 | border-color darken(red, 20%) 25 | color var(--theme-foreground-color) 26 | .custom-block-title 27 | color darken(red, 40%) 28 | a 29 | color var(--theme-foreground-color) 30 | &.details 31 | display block 32 | position relative 33 | border-radius 2px 34 | margin 1.6em 0 35 | padding 1.6em 36 | background-color var(--theme-accent-color-005) 37 | h4 38 | margin-top 0 39 | figure, p 40 | &:last-child 41 | margin-bottom 0 42 | padding-bottom 0 43 | summary 44 | outline none 45 | cursor pointer -------------------------------------------------------------------------------- /styles/fonts.styl: -------------------------------------------------------------------------------- 1 | // https://fonts.googleapis.com/css?family=Baloo 2 | /* latin */ 3 | @font-face { 4 | font-weight: 400; 5 | font-style: normal; 6 | font-family: 'Baloo Bhaijaan'; 7 | src: local('Baloo Bhaijaan Regular'), local('BalooBhaijaan-Regular'), url('~assets/fonts/google/bbr.woff2') format('woff2'); 8 | 9 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; 10 | } 11 | 12 | // https://fonts.googleapis.com/css?family=Josefin+Sans 13 | /* latin */ 14 | @font-face { 15 | font-weight: 400; 16 | font-style: normal; 17 | font-family: 'Josefin Sans'; 18 | src: local('Josefin Sans Regular'), local('JosefinSans-Regular'), url('~assets/fonts/google/jsr.woff2') format('woff2'); 19 | 20 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215; 21 | } 22 | 23 | // https://fonts.googleapis.com/css?family=Montserrat:300 24 | /* latin */ 25 | @font-face { 26 | font-weight: 300; 27 | font-style: normal; 28 | font-family: 'Montserrat'; 29 | src: local('Montserrat Light'), local('Montserrat-Light'), url('~assets/fonts/google/ml.woff2') format('woff2'); 30 | 31 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 32 | } 33 | 34 | // https://fonts.googleapis.com/css?family=Inconsolata 35 | /* latin */ 36 | @font-face { 37 | font-weight: 400; 38 | font-style: normal; 39 | font-family: 'Inconsolata'; 40 | src: local('Inconsolata Regular'), local('Inconsolata-Regular'), url('~assets/fonts/google/ir.woff2') format('woff2'); 41 | 42 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 43 | } 44 | 45 | // https://fonts.googleapis.com/css?family=Sarpanch 46 | /* latin */ 47 | @font-face { 48 | font-family: 'Sarpanch'; 49 | font-style: normal; 50 | font-weight: 400; 51 | src: url('~assets/fonts/google/sarpanch.woff2') format('woff2'); 52 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 53 | } 54 | 55 | // https://fonts.googleapis.com/css?family=Lobster 56 | /* latin */ 57 | @font-face { 58 | font-family: 'Lobster'; 59 | font-style: normal; 60 | font-weight: 400; 61 | src: url('~assets/fonts/google/lobster.woff2') format('woff2'); 62 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 63 | } 64 | 65 | // Icon 66 | @font-face { 67 | font-weight: 400; 68 | font-style: normal; 69 | font-family: Icon; 70 | src: url('~assets/fonts/icomoon/Icon.woff') format("woff") 71 | } -------------------------------------------------------------------------------- /styles/index.styl: -------------------------------------------------------------------------------- 1 | @require './fonts' 2 | @require './code' 3 | @require './custom_block' 4 | * { 5 | box-sizing border-box 6 | } 7 | ::selection 8 | background-color var(--theme-accent-color) 9 | color: #fff 10 | html 11 | min-height 100vh 12 | body 13 | margin 0 14 | body, html 15 | font-size 14px 16 | #app 17 | height 100% 18 | overflow-x: hidden; 19 | body 20 | font-family var(--theme-font-base) 21 | font-display swap 22 | color var(--theme-foreground-color) 23 | background var(--theme-background) 24 | background-attachment fixed 25 | text-rendering: geometricPrecision; 26 | -webkit-font-smoothing: antialiased; 27 | -webkit-text-size-adjust: 100%; 28 | a 29 | color: inherit 30 | text-decoration-line: none; 31 | &:link, &:visited 32 | color var(--theme-foreground-color) 33 | &:hover, &:active 34 | color var(--theme-accent-color) 35 | blockquote 36 | font-size 1rem 37 | color #999; 38 | border-left .2rem solid #dfe2e5 39 | margin 1rem 0 40 | padding .25rem 0 .25rem 1rem 41 | 42 | & > p 43 | margin 0 44 | 45 | ul, ol 46 | padding-left 1.2em 47 | .theme-container 48 | position relative 49 | height 100% 50 | .theme-main 51 | margin-left 280px 52 | position relative 53 | .theme-main__inner 54 | position: relative; 55 | display: block; 56 | .router-link-active 57 | color var(--theme-accent-color) 58 | .tac 59 | text-align: center 60 | .mt-1 61 | margin-top 4px 62 | .mt-2 63 | margin-top 8px 64 | .mt-4 65 | margin-top 16px 66 | .mt-7 67 | margin-top 28px 68 | h1 69 | font-size: 1.78571rem; 70 | h1,h2,h3,h4,h5,h6 71 | font-family var(--theme-font-heading) 72 | font-weight: 400; 73 | padding-bottom 0 74 | border-bottom none 75 | line-height 1.5 76 | &:focus .header-anchor, 77 | &:hover .header-anchor 78 | opacity: 1 79 | a.header-anchor 80 | font-size 0.85em 81 | float left 82 | margin-left -0.87em 83 | padding-right 0.23em 84 | margin-top 0.125em 85 | opacity 0 86 | 87 | &:focus, 88 | &:hover 89 | text-decoration none 90 | img { 91 | max-width: 100%; 92 | } 93 | 94 | // icon 95 | [class^="icon-"] 96 | font-family Icon 97 | font-style normal 98 | .icon-email:before 99 | content: "\E913" 100 | .icon-github:before 101 | content: "\E92D"; 102 | .icon-qq:before 103 | content: "\E920"; 104 | .icon-feed:before 105 | content: "\E924"; 106 | .icon-calendar:before 107 | content: "\E90E"; 108 | .icon-author:before 109 | content: "\E903"; 110 | .icon-cate:before 111 | content: "\E904"; 112 | .icon-time:before 113 | content: "\E910"; 114 | .icon-sidebar:before 115 | content: "\E926"; 116 | .icon-down:before 117 | content: "\E90B"; 118 | .icon-up:before 119 | content "\E90C" 120 | .icon-toc:before 121 | content: "\E925"; 122 | .icon-search:before 123 | content: "\E90D"; 124 | .icon-exit:before 125 | content: "\E928"; 126 | .icon-folder-minus:before 127 | content "\e900"; 128 | .icon-folder-plus:before 129 | content "\e901"; 130 | .icon-minus-circle:before 131 | content "\e923"; 132 | .icon-minus-square:before 133 | content "\e92e"; 134 | .icon-minus:before 135 | content "\e92f"; 136 | .icon-plus-square:before 137 | content "\e930"; 138 | .icon-plus-circle:before 139 | content "\e931"; 140 | .icon-plus:before 141 | content "\e932"; 142 | .icon-external:before 143 | content "\e929"; 144 | .icon-check:before 145 | content "\e92a"; 146 | .icon-circle:before 147 | content "\e92b"; 148 | .icon-link:before 149 | content "\e906"; 150 | .icon-at:before 151 | content "\e927"; 152 | .icon-image:before 153 | content "\e905"; 154 | .icon-hash:before 155 | content "\e907"; 156 | .icon-dot:before 157 | content "\e908"; 158 | .icon-next:before 159 | content "\e909"; 160 | .icon-previous:before 161 | content "\e90a"; 162 | .icon-eye:before 163 | content "\e90f"; 164 | .icon-clock:before 165 | content "\e910"; 166 | .icon-loader:before 167 | content "\e902"; 168 | .icon-wechat:before 169 | content "\e91c"; 170 | .icon-paypal:before 171 | content "\e919"; 172 | .icon-bitcoin:before 173 | content "\e91b"; 174 | .icon-soundcloud:before 175 | content "\e911"; 176 | .icon-pinterest:before 177 | content "\e91a"; 178 | .icon-youtube:before 179 | content "\e916"; 180 | .icon-tumblr:before 181 | content "\e914"; 182 | .icon-linkedin:before 183 | content "\e915"; 184 | .icon-myspace:before 185 | content "\e917"; 186 | .icon-disqus:before 187 | content "\e918"; 188 | .icon-hangouts:before 189 | content "\e912"; 190 | .icon-facebook:before 191 | content "\e91d"; 192 | .icon-dribbble:before 193 | content "\e91e"; 194 | .icon-telegram:before 195 | content "\e91f"; 196 | .icon-instagram:before 197 | content "\e921"; 198 | .icon-weibo:before 199 | content "\e922"; 200 | .icon-twitter:before 201 | content "\e92c"; 202 | // post list 203 | .post-list 204 | &__item 205 | position relative 206 | display flex 207 | padding .5rem 0 208 | padding-left 1.5rem 209 | line-height 2rem 210 | align-items center 211 | &:after 212 | position absolute 213 | content '-' 214 | left 0 215 | width 1.5rem 216 | text-align center 217 | &__date 218 | letter-spacing 1px 219 | font-size .85rem 220 | opacity .63 221 | padding-right .5rem 222 | font-family var(--theme-font-label) 223 | &__title 224 | font-size 1.15rem 225 | 226 | // pagination 227 | #app .pagination 228 | margin-top 2rem 229 | margin-bottom 2rem 230 | display: flex 231 | justify-content: center 232 | #app .pagination > li 233 | &.disabled 234 | display: none 235 | &.page-item 236 | &.active > a 237 | background var(--theme-accent-color) 238 | color #ffffff 239 | > a 240 | font-family var(--theme-font-label) 241 | margin: 0 3px; 242 | padding 0 243 | text-align center 244 | line-height 2.4rem 245 | width: 2.4rem; 246 | height: 2.4rem; 247 | border-radius 50% 248 | border none 249 | background none 250 | &:hover 251 | background var(--theme-accent-color) 252 | color #ffffff 253 | &:first-child > a, &:last-child > a 254 | width: 2.4rem; 255 | height: 2.4rem; 256 | text-align center 257 | line-height 2.4rem 258 | padding 0 259 | margin 0 3px 260 | border-radius 50% 261 | background none 262 | border none 263 | &:hover 264 | background var(--theme-accent-color) 265 | color #ffffff 266 | &:first-child > a:after, &:last-child > a:after 267 | text-align center 268 | font-family Icon 269 | &:first-child > a:after 270 | content "\E90A" 271 | &:last-child > a:after 272 | content "\E909" 273 | 274 | // post 275 | // 动画 276 | @keyframes line-scale { 277 | 0% { 278 | transform: scale3d(.8,.8,1) 279 | } 280 | 281 | to { 282 | transform: none 283 | } 284 | } 285 | // 评论 286 | .vssue 287 | color var(--theme-foreground-color) 288 | opacity .8 289 | .vssue-new-comment, .vssue-header 290 | border-bottom-color var(--theme-border-color) 291 | .vssue-comment-avatar img 292 | border-radius: 50% 293 | // 滚动条 294 | ::-webkit-scrollbar 295 | width: 8px; 296 | height: 6px; 297 | ::-webkit-scrollbar-thumb 298 | background-color var(--theme-accent-color-02) 299 | border-radius: 3px; 300 | &:hover 301 | background-color var(--theme-accent-color-04) 302 | ::-webkit-scrollbar-track 303 | background var(--theme-card-background) 304 | -webkit-box-shadow: inset 0 0 5px rgba(0,0,0,0.08); 305 | .medium-zoom-overlay 306 | background: var(--theme-card-background)!important 307 | // 覆盖nprogress进度条颜色 308 | #nprogress 309 | .bar 310 | background var(--theme-accent-color)!important 311 | .peg 312 | box-shadow 0 0 10px var(--theme-accent-color), 0 0 5px var(--theme-accent-color)!important 313 | // 覆盖pagination分页颜色 314 | #app 315 | .pagination > li > a, 316 | .pagination > li > span 317 | color var(--theme-accent-color) -------------------------------------------------------------------------------- /styles/mobile.styl: -------------------------------------------------------------------------------- 1 | @media (max-width: $MQNarrow) 2 | .theme-palette 3 | display flex 4 | .theme-main 5 | margin-left 0 6 | transform-origin: 0 42%; 7 | transition-property transform border-radius 8 | transition-duration .4s 9 | &__inner 10 | margin 0 auto 11 | .theme-sidebar 12 | z-index 0 13 | opacity 0 14 | border-right none 15 | transition-property visibility, opacity 16 | visibility: hidden; 17 | transition-duration .4s 18 | .theme-container 19 | transition-delay .2s 20 | transition-property background 21 | transition-duration .4s 22 | &.sidebar-open 23 | .theme-sidebar 24 | visibility: visible; 25 | opacity 1 26 | .theme-main 27 | border-radius: .85rem; 28 | transform: translate3d(302px, 0px, 0px); 29 | &__inner 30 | height 100vh 31 | overflow hidden 32 | @media (min-width: $MQMobile) 33 | .theme-main__inner 34 | margin 2rem auto 35 | max-width 660px 36 | min-width 640px 37 | .theme-search__inner 38 | max-width: var(--theme-content-width); 39 | @media (max-width: $MQMobile) 40 | .theme-main 41 | transform-origin: 0 42%; 42 | transition-property transform border-radius 43 | transition-duration .4s 44 | &__inner 45 | margin 0 auto 46 | .theme-sidebar 47 | border-right none 48 | transition-property visibility, opacity 49 | transition-duration .4s 50 | .theme-container 51 | transition-delay .2s 52 | transition-property background 53 | transition-duration .4s 54 | &.sidebar-open 55 | background var(--theme-sidebar-background) 56 | .theme-sidebar 57 | opacity 1 58 | .theme-main 59 | border-radius: .85rem; 60 | transform: translate3d(302px, 0px, 0px) scale3d(0.86, 0.86, 1); 61 | &__inner 62 | height 100vh 63 | overflow hidden 64 | &:before 65 | position: absolute; 66 | border-radius: .85rem; 67 | top: 1.6rem; 68 | bottom: 1.6rem; 69 | left: -1.6rem; 70 | z-index: -1; 71 | width: 100%; 72 | background-color: hsla(0,0%,100%,.2); 73 | content: ""; 74 | .article-item 75 | margin 1rem!important 76 | .theme-sidebar__inner 77 | .theme-header 78 | background: none 79 | &__name, &__slogan 80 | color: #ffffff 81 | .theme-SubNav 82 | background: none 83 | .theme-footer 84 | width 100% 85 | color #ffffff 86 | position static 87 | text-align center 88 | padding: 1rem 1rem .75rem; 89 | a:link, a:visited 90 | color #ffffff 91 | .theme-nav 92 | .search-box 93 | padding-left: 3rem; 94 | padding-right 3rem 95 | text-align: left; 96 | input 97 | width 100% 98 | left 0 99 | background-color: #fff 100 | .nav-links .nav-item 101 | padding-left: 3rem; 102 | text-align: left; 103 | a:link, a:visited 104 | color #ffffff 105 | a:after 106 | content: none 107 | .theme-search 108 | &__inner 109 | margin-top 0 110 | border-radius 0 111 | .search-box 112 | .suggestions 113 | border-radius 0 114 | -------------------------------------------------------------------------------- /styles/palette.styl: -------------------------------------------------------------------------------- 1 | // light colors 2 | $accentColor = #607d8b 3 | $textColor = #363636 4 | $borderColor = #e0e0e0 5 | $arrowBgColor = #ccc 6 | 7 | // dark colors 8 | $dark_accentColor = #607d8b 9 | $dark_textColor = #d8d8d8 10 | $dark_borderColor = #444 11 | $dark_cardBackgroundColor = #252525 12 | 13 | // badge color 14 | $badgeTipColor = $accentColor 15 | $badgeWarningColor = darken(#ffe564, 35%) 16 | $badgeErrorColor = #DA5961 17 | 18 | // content 19 | $contentClass = '.article-content' 20 | $descClass = '.article-desc' 21 | 22 | // // layout 23 | // $navbarHeight = 3.6rem 24 | $sidebarWidth = 20rem 25 | // $contentWidth = 740px 26 | // $homePageWidth = 960px 27 | 28 | 29 | // // responsive breakpoints 30 | $MQNarrow = 975px 31 | $MQMobile = 675px 32 | $MQMobileNarrow = 359px -------------------------------------------------------------------------------- /templates/dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /templates/ssr.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | {{ title }} 7 | 8 | 9 | {{{ userHeadTags }}} 10 | {{{ pageMeta }}} 11 | {{{ canonicalLink }}} 12 | {{{ renderResourceHints() }}} 13 | {{{ renderStyles() }}} 14 | 15 | 16 | 17 | {{{ renderScripts() }}} 18 | 19 | -------------------------------------------------------------------------------- /util/index.js: -------------------------------------------------------------------------------- 1 | export const hashRE = /#.*$/ 2 | export const extRE = /\.(md|html)$/ 3 | export const endingSlashRE = /\/$/ 4 | export const outboundRE = /^[a-z]+:/i 5 | 6 | export function normalize (path) { 7 | return decodeURI(path) 8 | .replace(hashRE, '') 9 | .replace(extRE, '') 10 | } 11 | 12 | export function getCssVar(key) { 13 | return window.getComputedStyle(document.documentElement).getPropertyValue(key).trim(); 14 | } 15 | 16 | /* 17 | * find parent vm by ref 18 | * @param {String} ref 19 | * @param {Vue} vm 20 | * @param {any} def default value 21 | * @returns {Element} 22 | */ 23 | export function findContainerInVm(ref, vm, def) { 24 | if (!ref) return def 25 | let container 26 | let parent = vm 27 | while ((parent = parent.$parent) && !container) { 28 | container = parent.$refs[ref] 29 | } 30 | // Ensure it's html element (ref could be component) 31 | if (container && container.$el) { 32 | container = container.$el 33 | } 34 | return container || def 35 | } 36 | 37 | export function isExternal (path) { 38 | return outboundRE.test(path) 39 | } 40 | 41 | export function isMailto (path) { 42 | return /^mailto:/.test(path) 43 | } 44 | 45 | export function isTel (path) { 46 | return /^tel:/.test(path) 47 | } 48 | 49 | export function ensureExt (path) { 50 | if (isExternal(path)) { 51 | return path 52 | } 53 | const hashMatch = path.match(hashRE) 54 | const hash = hashMatch ? hashMatch[0] : '' 55 | const normalized = normalize(path) 56 | 57 | if (endingSlashRE.test(normalized)) { 58 | return path 59 | } 60 | return normalized + '.html' + hash 61 | } 62 | 63 | export function resolveNavLinkItem (linkItem) { 64 | return Object.assign(linkItem, { 65 | type: linkItem.items && linkItem.items.length ? 'links' : 'link' 66 | }) 67 | } 68 | 69 | /** 70 | * @param { Route } route 71 | * @param { Array | Array | [link: string]: SidebarConfig } config 72 | * @returns { base: string, config: SidebarConfig } 73 | */ 74 | export function resolveMatchingConfig (regularPath, config) { 75 | if (Array.isArray(config)) { 76 | return { 77 | base: '/', 78 | config: config 79 | } 80 | } 81 | for (const base in config) { 82 | if (ensureEndingSlash(regularPath).indexOf(encodeURI(base)) === 0) { 83 | return { 84 | base, 85 | config: config[base] 86 | } 87 | } 88 | } 89 | return {} 90 | } 91 | 92 | /** 93 | * @param { Page } page 94 | * @returns { SidebarGroup } 95 | */ 96 | function resolveHeaders (page) { 97 | const headers = groupHeaders(page.headers || []) 98 | return [{ 99 | type: 'group', 100 | collapsable: false, 101 | title: page.title, 102 | path: null, 103 | children: headers.map(h => ({ 104 | type: 'auto', 105 | title: h.title, 106 | basePath: page.path, 107 | path: page.path + '#' + h.slug, 108 | children: h.children || [] 109 | })) 110 | }] 111 | } 112 | 113 | /** 114 | * @param { Page } page 115 | * @param { string } regularPath 116 | * @param { SiteData } site 117 | * @param { string } localePath 118 | * @returns { SidebarGroup } 119 | */ 120 | export function resolveSidebarItems (page, regularPath, site, localePath) { 121 | const { pages, themeConfig } = site 122 | 123 | const localeConfig = localePath && themeConfig.locales 124 | ? themeConfig.locales[localePath] || themeConfig 125 | : themeConfig 126 | 127 | const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar 128 | if (pageSidebarConfig === 'auto') { 129 | return resolveHeaders(page) 130 | } 131 | 132 | const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar 133 | if (!sidebarConfig) { 134 | return [] 135 | } else { 136 | const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig) 137 | if (config === 'auto') { 138 | return resolveHeaders(page) 139 | } 140 | return config 141 | ? config.map(item => resolveItem(item, pages, base)) 142 | : [] 143 | } 144 | } 145 | 146 | function resolveItem (item, pages, base, groupDepth = 1) { 147 | if (typeof item === 'string') { 148 | return resolvePage(pages, item, base) 149 | } else if (Array.isArray(item)) { 150 | return Object.assign(resolvePage(pages, item[0], base), { 151 | title: item[1] 152 | }) 153 | } else { 154 | const children = item.children || [] 155 | if (children.length === 0 && item.path) { 156 | return Object.assign(resolvePage(pages, item.path, base), { 157 | title: item.title 158 | }) 159 | } 160 | return { 161 | type: 'group', 162 | path: item.path, 163 | title: item.title, 164 | sidebarDepth: item.sidebarDepth, 165 | initialOpenGroupIndex: item.initialOpenGroupIndex, 166 | children: children.map(child => resolveItem(child, pages, base, groupDepth + 1)), 167 | collapsable: item.collapsable !== false 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /util/node.js: -------------------------------------------------------------------------------- 1 | const { path, fileToPath } = require('@vuepress/shared-utils') 2 | const fs = require("fs"); 3 | function listFile(dir){ 4 | const list = []; 5 | const arr = fs.readdirSync(dir); 6 | arr.forEach(function(item){ 7 | const fullpath = path.join(dir,item); 8 | const stats = fs.statSync(fullpath); 9 | if(stats.isFile()){ 10 | list.push(fileToPath(item)); 11 | } 12 | }); 13 | return list; 14 | } 15 | 16 | module.exports = { 17 | listFile 18 | } --------------------------------------------------------------------------------