Commit 92454669 authored by Ori Pomerantz's avatar Ori Pomerantz

feat(docs/op-stack): Remove

Transitioned to its own repository at https://github.com/ethereum-optimism/stack-docs
parent 48816b07
# The OP Stack Docs
[![Discord](https://img.shields.io/discord/667044843901681675.svg?color=768AD4&label=discord&logo=https%3A%2F%2Fdiscordapp.com%2Fassets%2F8c9701b98ad4372b58f13fd9f65f966e.svg)](https://discord.gg/optimism)
[![Twitter Follow](https://img.shields.io/twitter/follow/optimismPBC.svg?label=optimismPBC&style=social)](https://twitter.com/optimismPBC)
The OP Stack is an open, collectively maintained development stack for blockchain ecosystems.
This repository contains the source code for the [OP Stack Docs](https://stack.optimism.io).
## Development
### Serving docs locally
```sh
yarn dev
```
Then navigate to [http://localhost:8080](http://localhost:8080).
If that link doesn't work, double check the output of `yarn dev`.
You might already be serving something on port 8080 and the site may be on another port (e.g., 8081).
### Building docs for production
```sh
yarn build
```
You probably don't need to run this command, but now you know.
### Editing docs
Edit the markdown directly in [src/docs](./src/docs).
### Adding new docs
Add your markdown files to [src/docs](./src/docs).
You will also have to update [src/.vuepress/config.js](./src/.vuepress/config.js) if you want these docs to show up in the sidebar.
### Updating the theme
We currently use an ejected version of [vuepress-theme-hope](https://vuepress-theme-hope.github.io/).
Since the version we use was ejected from the original theme, you'll see a bunch of compiled JavaScript files instead of the original TypeScript files.
There's not much we can do about that right now, so you'll just need to make do and edit the raw JS if you need to make theme adjustments.
We're planning to move away from VuePress relatively soon anyway so we won't be fixing this.
{
"name": "@eth-optimism/op-stack-docs",
"version": "0.0.2",
"description": "The OP Stack Docs",
"main": "index.js",
"scripts": {
"dev": "vuepress dev src",
"build": "vuepress build src",
"preview": "yarn build && serve -s src/.vuepress/dist -p 8080"
},
"license": "MIT",
"devDependencies": {
"@vuepress/plugin-medium-zoom": "^1.8.2",
"@vuepress/plugin-pwa": "^1.9.7",
"serve": "^14.2.0",
"vuepress": "^1.8.2",
"vuepress-plugin-plausible-analytics": "^0.2.1",
"vuepress-theme-hope": "^1.22.0"
},
"dependencies": {
"check-md": "^1.0.2",
"dayjs": "^1.11.7"
}
}
const { description } = require('../../package')
const path = require('path')
module.exports = {
title: 'OP Stack Docs',
description: description,
head: [
['link', { rel: 'manifest', href: '/manifest.json' }],
['meta', { name: 'theme-color', content: '#3eaf7c' }],
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }],
['meta', { property: 'og:image', content: 'https://stack.optimism.io/assets/logos/twitter-logo.png' }],
['meta', { name: 'twitter:image', content: 'https://stack.optimism.io/assets/logos/twitter-logo.png' }],
['meta', { name: 'twitter:title', content: 'OP Stack Docs' }],
['meta', { property: 'og:title', content: 'OP Stack Docs' }],
['meta', { name: 'twitter:card', content: 'summary' } ],
['link', { rel: "icon", type: "image/png", sizes: "32x32", href: "/assets/logos/favicon.png"}],
],
theme: path.resolve(__dirname, './theme'),
themeConfig: {
"twitter:card": "summary",
contributor: false,
hostname: 'https://stack.optimism.io',
logo: '/assets/logos/logo.png',
docsDir: 'src',
docsRepo: 'https://github.com/ethereum-optimism/opstack-docs',
docsBranch: 'main',
lastUpdated: false,
darkmode: 'disable',
themeColor: false,
blog: false,
iconPrefix: 'far fa-',
pageInfo: false,
pwa: {
cacheHTML: false,
},
activeHash: {
offset: -200,
},
algolia: {
appId: 'O9WKE9RMCV',
apiKey: '00cf17cba30b374d08d7f7afead974be',
indexName: 'optimism'
},
nav: [
{
text: 'Home',
link: 'https://www.optimism.io/'
},
{
text: 'OP Stack Docs',
link: '/'
},
{
text: 'Optimism Docs',
link: 'https://community.optimism.io/'
},
{
text: 'Governance',
link: 'https://community.optimism.io/docs/governance/'
},
{
text: 'Community',
items: [
{
icon: 'discord',
iconPrefix: 'fab fa-',
iconClass: 'color-discord',
text: 'Discord',
link: 'https://discord.optimism.io',
},
{
icon: 'github',
iconPrefix: 'fab fa-',
iconClass: 'color-github',
text: 'GitHub',
link: 'https://github.com/ethereum-optimism/optimism',
},
{
icon: 'twitter',
iconPrefix: 'fab fa-',
iconClass: 'color-twitter',
text: 'Twitter',
link: 'https://twitter.com/optimismFND',
},
{
icon: 'twitch',
iconPrefix: 'fab fa-',
iconClass: 'color-twitch',
text: 'Twitch',
link: 'https://www.twitch.tv/optimismpbc'
},
{
icon: 'medium',
iconPrefix: 'fab fa-',
iconClass: 'color-medium',
text: 'Blog',
link: 'https://optimismpbc.medium.com/'
},
{
icon: 'computer-classic',
iconClass: 'color-ecosystem',
text: 'Ecosystem',
link: 'https://www.optimism.io/apps/all',
},
{
icon: 'globe',
iconClass: 'color-optimism',
text: 'optimism.io',
link: 'https://www.optimism.io/',
}
]
}
],
searchPlaceholder: 'Search the docs',
sidebar: [
{
title: "OP Stack",
collapsable: false,
children: [
'/',
[
'/docs/understand/design-principles.md',
'Design Principles'
],
'/docs/understand/landscape.md',
'/docs/understand/explainer.md'
]
},
{
title: "Releases",
collapsable: false,
children: [
'/docs/releases/',
{
title: "Bedrock",
collapsable: true,
children: [
'/docs/releases/bedrock/',
'/docs/releases/bedrock/explainer.md',
'/docs/releases/bedrock/differences.md'
]
}
]
},
{
title: "Building OP Stack Rollups",
collapsable: false,
children: [
'/docs/build/getting-started.md',
'/docs/build/conf.md',
'/docs/build/operations.md',
'/docs/build/explorer.md',
'/docs/build/sdk.md',
{
title: "OP Stack Hacks",
collapsable: true,
children: [
'/docs/build/hacks.md',
'/docs/build/featured.md',
'/docs/build/data-avail.md',
'/docs/build/derivation.md',
'/docs/build/execution.md',
'/docs/build/settlement.md',
{
title: "Sample Hacks",
children: [
"/docs/build/tutorials/add-attr.md",
"/docs/build/tutorials/new-precomp.md",
"/docs/build/tutorials/predeploys.md"
]
} // End of tutorials
],
}, // End of OP Stack hacks
],
}, // End of Building OP Stack Rollups
{
title: "Contributing",
collapsable: false,
children: [
'/docs/contribute.md',
]
},
{
title: "Security",
collapsable: false,
children: [
'/docs/security/faq.md',
'/docs/security/policy.md',
'/docs/security/pause.md',
'/docs/security/forced-withdrawal.md',
]
},
], // end of sidebar
plugins: [
"@vuepress/pwa",
[
'@vuepress/plugin-medium-zoom',
{
selector: ':not(a) > img'
}
],
"plausible-analytics"
]
}
}
// module.exports.themeConfig.sidebar["/docs/useful-tools/"] = module.exports.themeConfig.sidebar["/docs/developers/"]
import event from '@vuepress/plugin-pwa/lib/event'
export default ({ router }) => {
registerAutoReload();
router.addRoutes([
{ path: '/docs/', redirect: '/' },
])
}
// When new content is detected by the app, this will automatically
// refresh the page, so that users do not need to manually click
// the refresh button. For more details see:
// https://linear.app/optimism/issue/FE-1003/investigate-archive-issue-on-docs
const registerAutoReload = () => {
event.$on('sw-updated', e => {
e.skipWaiting().then(() =>
{
if (typeof location !== 'undefined')
location.reload(true);
}
)
})
}
{
"name": "OP Docs",
"short_name": "OP Docs",
"description": "The official OP Docs",
"icons": [
{
"src": "/assets/logos/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/assets/logos/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/assets/logos/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/assets/logos/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/assets/logos/icon-1020x1020.png",
"sizes": "any",
"type": "image/png"
}
],
"start_url": "/index.html",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#ff0420"
}
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;600&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,400;0,600;0,700;1,600;1,700&display=swap');
@import 'https://pro.fontawesome.com/releases/v5.15.4/css/all.css';
main, body, html {
font-family: 'Open Sans', sans-serif;
}
p {
font-size: 16px;
line-height: 24px;
}
aside.sidebar {
background-color: #F1F4F9;
border-right: none;
}
p.sidebar-heading {
color: #323A43 !important;
font-family: 'Open Sans', sans-serif;
font-weight: 600 !important;
font-size: 14px !important;
line-height: 24px !important;
min-height: 36px;
margin-left: 20px;
padding: 8px 16px !important;
width: calc(100% - 60px) !important;
border-radius: 8px;
}
a.sidebar-link {
font-family: 'Open Sans', sans-serif;
font-size: 14px !important;
line-height: 24px !important;
min-height: 36px;
margin-top: 3px;
margin-left: 20px;
padding: 8px 16px !important;
width: calc(100% - 60px) !important;
border-radius: 8px;
}
section.sidebar-group a.sidebar-link,
section.sidebar-group p.sidebar-heading.clickable {
margin-left: 32px;
width: calc(100% - 60px) !important;
}
.sidebar-links:not(.sidebar-group-items) > li > a.sidebar-link {
font-weight: 600 !important;
color: #323A43 !important;
}
.sidebar-links:not(.sidebar-group-items) > li > a.sidebar-link.active {
border-left-color: #F1F4F9 !important;
background-color: #FFDBDF !important;
color: #FF0420 !important;
}
a.sidebar-link.active {
border-left-color: #F1F4F9 !important;
background-color: #FFDBDF !important;
color: #FF0420 !important;
}
h1 {
font-size: 50px;
}
h2 {
font-size: 40px;
}
h3 {
font-size: 28px;
}
h4 {
font-size: 20px;
}
h1 {
font-family: 'Rubik', sans-serif;
font-weight: 700;
font-style: italic;
border-bottom: none;
color: #202327 !important;
}
h2, h3, h4 {
font-family: 'Rubik', sans-serif;
border-bottom: none;
font-weight: 400;
}
#search-form {
@media (min-width $MQNormal) {
margin-left: 2rem;
}
}
.search-box {
@media (min-width $MQNormal) {
order: 1;
margin-right: 0;
margin-left: 1rem;
.suggestions {
left: auto !important;
right: 0 !important;
}
}
input {
border-color: #CBD5E0 !important;
border-radius: 100px !important;
background-color: #FFFFFF !important;
}
}
header.navbar {
border-bottom: none;
box-shadow: 0px 6px 8px -6px rgba(20, 23, 26, 0.06), 0px 8px 16px -6px rgba(20, 23, 26, 0.04);
--bgcolor-blur: hsla(0,0%,100%,0.9);
}
span.site-name {
display: none !important;
}
a.nav-link,
div.nav-item span.title {
font-weight: 600;
}
a.nav-link:not(.router-link-active),
div.nav-item span.title {
color: #68778D;
}
.theme-default-content:not(.custom) > p {
text-align: inherit !important;
}
.sidebar {
box-shadow: none !important;
}
div.hero-info {
color: white;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
}
div.hero-info .description {
display: none;
}
div.hero-info #main-title {
color: white !important;
}
header.hero > img {
border-radius: 16px;
max-height: none !important;
}
header.hero {
margin-top: 20px;
position: relative;
justify-content: space-between !important;
}
.home .features {
border-top: none !important;
justify-content: space-between !important;
margin: 0 !important;
padding-top: 0.5rem !important;
}
.home .features h2 {
font-family: 'Open Sans', sans-serif;
font-style: normal;
font-size: 16px !important;
font-weight: 600 !important;
color: #202327 !important;
}
.home .features p {
font-family: 'Open Sans', sans-serif;
font-size: 14px !important;
color: #68778D !important;
}
.home .features .icon-container {
height: 44px;
width: 44px;
background-color: #FFF0F1;
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
color: #FF0420;
}
.home .features .feature {
background-color: #FFFFFF !important;
box-shadow: 0px 6px 8px -6px rgba(20, 23, 26, 0.12), 0px 8px 16px -6px rgba(20, 23, 26, 0.08);
border-radius: 16px !important;
margin: 0 !important;
margin-bottom: 2rem !important;
padding: 1.5rem !important;
}
.features-header {
margin-top: 30px;
margin-bottom: 0px;
}
div.theme-container:not(.has-sidebar) {
background-color: #F1F4F9;
}
.anchor-header {
color: #202327;
font-weight: 600;
font-size: 14px;
font-height: 20px;
margin-bottom: 10px;
}
.anchor-support {
margin-top: 20px;
}
.anchor-support-links i {
width: 20px;
text-align: center;
margin-right: 5px;
}
.anchor-support-links a {
color: #68778D;
}
.anchor-support-links a div {
height: 30px;
font-size: 14px;
}
.anchor-support-links a:hover {
color: #FF0420;
}
#anchor {
width: 15rem !important;
}
#anchor .anchor {
line-height: 27.2px !important;
}
#anchor .anchor div {
color: #68778D !important;
}
#anchor .anchor.active div {
font-weight: 600 !important;
}
.theme-default-content code {
top: 1px;
line-height: 22.4px !important;
vertical-align: middle !important;
}
.nav-dropdown svg.icon.outbound {
display: none;
}
.nav-dropdown .dropdown-item i {
width: 20px;
}
.color-discord {
color: #5865F2;
}
.color-github {
color: #121212;
}
.color-twitter {
color: #1DA1F2;
}
.color-twitch {
color: #6441A5;
}
.color-medium {
color: #000000;
}
.color-optimism {
color: #FF0420;
}
.color-ecosystem {
color: #ea94db;
}
$textColor = #000000
$accentColor = #f01a37
$backgroundColor = #272934
$lightBackgroundColor = #f3f3f3
$sidebarWidth = 320px
import Vue from "vue";
import type { AlgoliaOption } from "@mr-hope/vuepress-types";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
placeholder: string;
}, {
initialize(userOptions: AlgoliaOption, lang: string): void;
update(options: AlgoliaOption, lang: string): void;
}, unknown, {
options: AlgoliaOption;
}>;
export default _default;
import Vue from "vue";
export default Vue.extend({
name: "AlgoliaSearchDropdown",
props: {
options: { type: Object, required: true },
},
data: () => ({
placeholder: "",
}),
watch: {
$lang(newValue) {
this.update(this.options, newValue);
},
options(newValue) {
this.update(newValue, this.$lang);
},
},
mounted() {
this.initialize(this.options, this.$lang);
this.placeholder =
this.$site.themeConfig.searchPlaceholder || "";
},
methods: {
initialize(userOptions, lang) {
void Promise.all([
import(
/* webpackChunkName: "docsearch" */ "docsearch.js/dist/cdn/docsearch.min.js"),
import(
/* webpackChunkName: "docsearch" */ "docsearch.js/dist/cdn/docsearch.min.css"),
]).then(([docsearch]) => {
// eslint-disable-next-line
docsearch.default(Object.assign(Object.assign({}, userOptions), { inputSelector: "#algolia-search-input",
// #697 Make docsearch work well at i18n mode.
algoliaOptions: {
facetFilters: [`lang:${lang}`].concat(
// eslint-disable-next-line
userOptions.facetFilters || []),
}, handleSelected: (_input, _event, suggestion) => {
const { pathname, hash } = new URL(suggestion.url);
const routepath = pathname.replace(this.$site.base, "/");
if (this.$router.getRoutes().some((route) => route.path === routepath))
void this.$router.push(`${routepath}${decodeURIComponent(hash)}`);
else
window.open(suggestion.url);
} }));
});
},
update(options, lang) {
this.$el.innerHTML =
'<input id="algolia-search-input" class="search-query">';
this.initialize(options, lang);
},
},
});
//# sourceMappingURL=Dropdown.js.map
\ No newline at end of file
{"version":3,"file":"Dropdown.js","sourceRoot":"","sources":["Dropdown.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AAKtB,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,uBAAuB;IAE7B,KAAK,EAAE;QACL,OAAO,EAAE,EAAE,IAAI,EAAE,MAAiC,EAAE,QAAQ,EAAE,IAAI,EAAE;KACrE;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,WAAW,EAAE,EAAE;KAChB,CAAC;IAEF,KAAK,EAAE;QACL,KAAK,CAAC,QAAgB;YACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,CAAC,QAAuB;YAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;KACF;IAED,OAAO;QACL,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1C,IAAI,CAAC,WAAW;YACb,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,iBAA4B,IAAI,EAAE,CAAC;IAC/D,CAAC;IAED,OAAO,EAAE;QACP,UAAU,CAAC,WAA0B,EAAE,IAAY;YACjD,KAAK,OAAO,CAAC,GAAG,CAAC;gBACf,MAAM;gBACJ,mCAAmC,CAAC,wCAAwC,CAC7E;gBACD,MAAM;gBACJ,mCAAmC,CAAC,yCAAyC,CAC9E;aACF,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,EAAE;gBACtB,2BAA2B;gBAC1B,SAAiB,CAAC,OAAO,iCACrB,WAAW,KACd,aAAa,EAAE,uBAAuB;oBACtC,8CAA8C;oBAC9C,cAAc,EAAE;wBACd,YAAY,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,MAAM;wBACnC,2BAA2B;wBACzB,WAAmB,CAAC,YAAyB,IAAI,EAAE,CACtD;qBACF,EACD,cAAc,EAAE,CACd,MAAwB,EACxB,MAAa,EACb,UAA2B,EAC3B,EAAE;wBACF,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;wBACnD,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;wBAEzD,IACE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC;4BAElE,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,SAAS,GAAG,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;;4BAC/D,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;oBACnC,CAAC,IACD,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QAED,MAAM,CAAC,OAAsB,EAAE,IAAY;YACzC,IAAI,CAAC,GAAG,CAAC,SAAS;gBAChB,wDAAwD,CAAC;YAC3D,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACjC,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<form
id="search-form"
class="algolia-search-wrapper search-box"
role="search"
>
<label class="sr-only" for="algolia-search-input">Algolia search</label>
<input
id="algolia-search-input"
class="search-query"
:placeholder="placeholder"
/>
</form>
</template>
<script src="./Dropdown" />
<style lang="stylus">
.algolia-search-wrapper
& > span
vertical-align middle
.algolia-autocomplete
line-height normal
.ds-dropdown-menu
min-width 515px !important
margin 6px 0 0
padding 4px
border 1px solid var(--light-grey)
border-radius 4px
background var(--bgcolor)
font-size 16px
text-align left
@media (max-width $MQMobile)
min-width calc(100vw - 4rem) !important
max-width calc(100vw - 4rem) !important
&:before
border-color var(--light-grey)
[class*=ds-dataset-]
padding 0
border none
background var(--bgcolor)
.ds-suggestions
margin-top 0
.ds-suggestion
border-bottom 1px solid var(--border-color)
.algolia-docsearch-suggestion--highlight
color var(--accent-color)
.algolia-docsearch-suggestion
padding 0
border-color var(--border-color)
background var(--bgcolor)
color var(--text-color)
.algolia-docsearch-suggestion--category-header
padding 5px 10px
margin-top 0
background var(--accent-color)
color var(--white)
font-weight 600
.algolia-docsearch-suggestion--highlight
background rgba(255, 255, 255, 0.6)
.algolia-docsearch-suggestion--wrapper
padding 0
@media (max-width $MQMobile)
padding 5px 7px 5px 5px !important
.algolia-docsearch-suggestion--title
margin-bottom 0
color var(--text-color)
font-weight 600
.algolia-docsearch-suggestion--subcategory-column
vertical-align top
padding 5px 7px 5px 5px
border-color var(--border-color)
background var(--bgcolor)
color var(--text-color)
@media (min-width $MQMobile)
display table-cell
float none
width 150px
min-width 150px
@media (max-width $MQMobile)
padding 0 !important
background white !important
&:after
display none
.algolia-docsearch-suggestion--subcategory-column-text
color #555
&:after
@media (max-width $MQMobile)
display inline-block
vertical-align middle
content ' > '
width 5px
margin -3px 3px 0
font-size 10px
line-height 14.4px
.algolia-docsearch-suggestion--content
@media (min-width $MQMobile)
display table-cell
float none
vertical-align top
width 100%
.algolia-docsearch-footer
border-color var(--border-color)
.ds-cursor .algolia-docsearch-suggestion--content
background var(--grey3)
color var(--text-color)
</style>
import Vue from "vue";
import type { AlgoliaOption } from "@mr-hope/vuepress-types";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
initialize(userOptions: AlgoliaOption, _lang: string): void;
resolveRoutePathFromUrl(absoluteUrl: string): string;
update(options: AlgoliaOption, lang: string): void;
}, unknown, {
options: AlgoliaOption;
}>;
export default _default;
import { createElement } from "preact";
import Vue from "vue";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: docsearch type issue
import docsearch from "@docsearch/js";
export default Vue.extend({
name: "AlgoliaSearchFull",
props: {
options: { type: Object, required: true },
},
watch: {
$lang(newValue) {
this.update(this.options, newValue);
},
options(newValue) {
this.update(newValue, this.$lang);
},
},
mounted() {
this.initialize(this.options, this.$lang);
},
methods: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
initialize(userOptions, _lang) {
// eslint-disable-next-line
docsearch(Object.assign(Object.assign({ container: "#docsearch", placeholder: this.$site.themeConfig.searchPlaceholder || "" }, userOptions), { searchParameters: userOptions.searchParameters || {},
// transform full url to route path
transformItems: (items) => items.map((item) => (Object.assign(Object.assign({}, item), {
// the `item.url` is full url with protocol and hostname
// so we have to transform it to vue-router path
url: this.resolveRoutePathFromUrl(item.url) }))),
// render the hit component with custom `onClick` handler
hitComponent: ({ hit, children }) => createElement("a", {
href: hit.url,
onClick: (event) => {
// We rely on the native link scrolling when user is
// already on the right anchor because Vue Router doesn’t
// support duplicated history entries.
if (this.$route.fullPath === hit.url)
return;
const fullPath = `${window.location.origin}${hit.url}`;
const { pathname: hitPathname } = new URL(fullPath);
// If the hits goes to another page, we prevent the native link behavior
// to leverage the Vue Router loading feature.
if (this.$route.path !== hitPathname)
event.preventDefault();
if (this.$router
.getRoutes()
.some((route) => route.path.replace(/index\.html$/, "") === hitPathname))
void this.$router.push(hit.url);
else
window.open(fullPath);
},
}, children), navigator: {
navigate: ({ itemUrl }) => {
const fullPath = `${window.location.origin}${itemUrl}`;
const { pathname: hitPathname } = new URL(fullPath);
// Vue Router doesn’t handle same-page navigation so we use
// the native browser location API for anchor navigation.
if (this.$route.path === hitPathname)
window.location.assign(fullPath);
else if (this.$router
.getRoutes()
.some((route) => route.path === hitPathname))
void this.$router.push(itemUrl);
else
window.open(fullPath);
},
navigateNewTab({ itemUrl }) {
window.open(itemUrl);
},
navigateNewWindow({ itemUrl }) {
window.open(itemUrl);
},
} }));
},
resolveRoutePathFromUrl(absoluteUrl) {
const { pathname, hash } = new URL(absoluteUrl);
return `${pathname.replace(this.$site.base, "/")}${hash}`;
},
update(options, lang) {
this.$el.innerHTML = '<div id="docsearch"></div>';
this.initialize(options, lang);
},
},
});
//# sourceMappingURL=Full.js.map
\ No newline at end of file
{"version":3,"file":"Full.js","sourceRoot":"","sources":["Full.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<template>
<div id="docsearch" />
</template>
<script src="./Full" />
<style lang="stylus">
@keyframes fade-in
0%
opacity 0
to
opacity 1
body
--docsearch-spacing 12px
--docsearch-icon-stroke-width 1.4
--docsearch-muted-color #969faf
--docsearch-container-background rgba(101, 108, 133, 0.8)
--docsearch-modal-width 560px
--docsearch-modal-height 600px
--docsearch-modal-shadow inset 1px 1px 0 0 hsla(0, 0%, 100%, 0.5), 0 3px 8px 0 #555a64
--docsearch-searchbox-height 56px
--docsearch-searchbox-background #ebedf0
--docsearch-searchbox-focus-background #efeef4
--docsearch-searchbox-shadow inset 0 0 0 2px var(--accent-color)
--docsearch-hit-height 56px
--docsearch-hit-color var(--dark-grey)
--docsearch-hit-shadow 0 1px 3px 0 #d4d9e1
--docsearch-key-gradient linear-gradient(-225deg, #d5dbe4, #f8f8f8)
--docsearch-key-shadow inset 0 -2px 0 0 #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px rgba(30, 35, 90, 0.4)
--docsearch-footer-height 44px
--docsearch-footer-shadow 0 -1px 0 0 #e0e3e8, 0 -3px 6px 0 rgba(69, 98, 155, 0.12)
@media (max-width $MQMobile)
--docsearch-searchbox-height 44px
--docsearch-spacing 8px
--docsearch-footer-height 40px
body.theme-dark
--docsearch-container-background rgba(9, 10, 17, 0.8)
--docsearch-modal-shadow inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309
--docsearch-searchbox-background #090a11
--docsearch-searchbox-focus-background lighten($darkBgColor, 10%)
--docsearch-hit-shadow none
--docsearch-key-gradient linear-gradient(-26.5deg, #565862, #31353b)
--docsearch-key-shadow inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, 0.3)
--docsearch-footer-shadow inset 0 1px 0 0 rgba(73, 76, 106, 0.5), 0 -4px 8px 0 rgba(0, 0, 0, 0.2)
--docsearch-muted-color #7f8497
.DocSearch-Button
display inline-flex
justify-content space-between
align-items center
height 36px
margin 0 1rem 0 0.25rem
padding 0.5rem
background var(--docsearch-searchbox-background)
border-radius 40px
color var(--docsearch-muted-color)
font-weight 500
user-select none
outline none
@media (max-width $MQMobile)
margin-right 0
&:active, &:focus, &:hover
background var(--docsearch-searchbox-focus-background)
box-shadow var(--docsearch-searchbox-shadow)
color var(--dark-grey)
&:hover .DocSearch-Search-Icon
@media (max-width $MQMobile)
color var(--accent-color)
.DocSearch-Button-Container
.navbar &
align-items center
display flex
.DocSearch-Search-Icon
width 1rem
height 1rem
margin 0.1rem
color #aaa
stroke-width 3
position relative
.DocSearch-Search-Icon
stroke-width 1.6
.DocSearch-Button-Placeholder
padding 0 12px 0 6px
font-size 1rem
.DocSearch-Button-Keys
.navbar &
display flex
.DocSearch-Button-Key
position relative
top -1px
width 20px
height 18px
margin-right 0.4em
padding-bottom 2px
border-radius 3px
background var(--docsearch-key-gradient)
box-shadow var(--docsearch-key-shadow)
color var(--docsearch-muted-color)
display flex
justify-content center
align-items center
.navbar &
display flex
.navbar
@media (max-width $MQNarrow)
.DocSearch-Button-Key, .DocSearch-Button-KeySeparator, .DocSearch-Button-Placeholder
display none
.DocSearch--active
overflow hidden !important
.DocSearch-Container
position fixed
top 0
left 0
z-index 200
box-sizing border-box
width 100vw
height 100vh
background-color var(--docsearch-container-background)
*
box-sizing border-box
a
text-decoration none
.DocSearch-Link
margin 0
padding 0
border 0
background none
color var(--accent-color)
font inherit
appearance none
cursor pointer
.DocSearch-Modal
position relative
max-width var(--docsearch-modal-width)
margin 60px auto auto
border-radius 6px
background var(--bgcolor)
box-shadow var(--docsearch-modal-shadow)
flex-direction column
@media (max-width $MQNarrow)
width 100%
max-width 100%
height 100vh
margin 0
border-radius 0
box-shadow none
.DocSearch-SearchBar
display flex
padding var(--docsearch-spacing) var(--docsearch-spacing) 0
.DocSearch-Form
align-items center
background var(--docsearch-searchbox-focus-background)
border-radius 8px
display flex
height var(--docsearch-searchbox-height)
padding 0 var(--docsearch-spacing)
position relative
width 100%
.DocSearch-Input
width 80%
height 100%
padding 0 0 0 8px
border 0
background transparent
color var(--text-color)
font inherit
font-size 1.2rem
appearance none
outline none
flex 1
&::placeholder
color var(--docsearch-muted-color)
opacity 1
&::-webkit-search-cancel-button, &::-webkit-search-decoration, &::-webkit-search-results-button, &::-webkit-search-results-decoration
display none
@media (max-width $MQMobile)
font-size 1rem
.DocSearch-LoadingIndicator, .DocSearch-MagnifierLabel, .DocSearch-Reset
margin 0
padding 0
.DocSearch-MagnifierLabel, .DocSearch-Reset
align-items center
color var(--accent-color)
display flex
justify-content center
.DocSearch-Container--Stalled .DocSearch-MagnifierLabel, .DocSearch-LoadingIndicator
display none
.DocSearch-Container--Stalled .DocSearch-LoadingIndicator
align-items center
color var(--accent-color)
display flex
justify-content center
.DocSearch-Reset
animation fade-in 0.1s ease-in forwards
appearance none
background none
border 0
border-radius 50%
color var(--docsearch-icon-color)
cursor pointer
padding 2px
right 0
stroke-width var(--docsearch-icon-stroke-width)
@media screen and (prefers-reduced-motion reduce)
animation none
appearance none
background none
border 0
border-radius 50%
color var(--docsearch-icon-color)
cursor pointer
right 0
stroke-width var(--docsearch-icon-stroke-width)
&[hidden]
display none
&:focus
outline none
&:hover
color var(--accent-color)
.DocSearch-LoadingIndicator svg, .DocSearch-MagnifierLabel svg
height 24px
width 24px
.DocSearch-Cancel
display none
margin-left var(--docsearch-spacing)
padding 0
border 0
background none
color var(--accent-color)
font-family Arial, Helvetica, sans-serif
font-size 1em
font-weight 500
white-space nowrap
user-select none
appearance none
cursor pointer
flex none
outline none
overflow hidden
@media (max-width $MQNarrow)
display inline-block
.DocSearch-Dropdown
max-height calc(var(--docsearch-modal-height) - var(--docsearch-searchbox-height) - var(--docsearch-spacing) - var(--docsearch-footer-height))
min-height var(--docsearch-spacing)
overflow-y auto
overflow-y overlay
padding 0 var(--docsearch-spacing)
@media (max-width $MQNarrow)
height 100%
max-height unset
& ul
list-style none
margin 0
padding 0
.DocSearch-Label
font-size 0.75em
line-height 1.6em
color var(--docsearch-muted-color)
.DocSearch-Help
color var(--docsearch-muted-color)
font-size 0.9em
margin 0
user-select none
.DocSearch-Title
font-size 1.2em
.DocSearch-Logo
a
display flex
svg
margin-left 8px
color var(--accent-color)
.DocSearch-Hits
&:last-of-type
margin-bottom 24px
mark
background none
color var(--accent-color)
.DocSearch-HitsFooter
margin-bottom var(--docsearch-spacing)
padding var(--docsearch-spacing)
color var(--docsearch-muted-color)
font-size 0.85em
display flex
justify-content center
a
border-bottom 1px solid
color inherit
.DocSearch-Hit
position relative
padding-bottom 4px
border-radius 4px
display flex
a
width 100%
padding-left var(--docsearch-spacing)
border-radius 4px
background var(--bgcolor)
box-shadow var(--docsearch-hit-shadow)
display block
&[aria-selected='true'] a
background-color var(--accent-color)
&[aria-selected='true'] mark
text-decoration underline
.DocSearch-Hit--deleting
opacity 0
transition all 0.25s linear
@media screen and (prefers-reduced-motion reduce)
transition none
.DocSearch-Hit--favoriting
transform scale(0)
transform-origin top center
transition all 0.25s linear
transition-delay 0.25s
@media screen and (prefers-reduced-motion reduce)
transition none
.DocSearch-Hit-source
position sticky
top 0
z-index 10
margin 0 -4px
padding 8px 4px 0
background var(--bgcolor-light)
color var(--accent-color)
font-size 0.85em
font-weight 600
line-height 32px
.DocSearch-Hit-Tree
width 24px
height var(--docsearch-hit-height)
color var(--docsearch-muted-color)
opacity 0.5
stroke-width var(--docsearch-icon-stroke-width)
@media (max-width $MQNarrow)
display none
.DocSearch-Hit-Container
height var(--docsearch-hit-height)
padding 0 var(--docsearch-spacing) 0 0
color var(--docsearch-hit-color)
display flex
flex-direction row
align-items center
.DocSearch-Hit-icon
width 20px
height 20px
color var(--docsearch-muted-color)
stroke-width var(--docsearch-icon-stroke-width)
.DocSearch-Hit-action
width 22px
height 22px
color var(--docsearch-muted-color)
stroke-width var(--docsearch-icon-stroke-width)
display flex
align-items center
svg
display block
width 18px
height 18px
& + &
margin-left 6px
.DocSearch-Hit-action-button
padding 2px
border 0
border-radius 50%
background none
color inherit
cursor pointer
appearance none
&:focus, &:hover
background rgba(0, 0, 0, 0.2)
transition background-color 0.1s ease-in
@media screen and (prefers-reduced-motion reduce)
background rgba(0, 0, 0, 0.2)
transition none
path
fill #fff
svg.DocSearch-Hit-Select-Icon
display none
.DocSearch-Hit[aria-selected='true'] .DocSearch-Hit-Select-Icon
display block
.DocSearch-Hit-content-wrapper
width 80%
position relative
margin 0 8px
font-weight 500
line-height 1.2em
text-overflow ellipsis
white-space nowrap
display flex
flex 1 1 auto
flex-direction column
justify-content center
overflow-x hidden
.DocSearch-Hit-title
font-size 0.9em
.DocSearch-Hit-path
color var(--docsearch-muted-color)
font-size 0.75em
.DocSearch-Hit[aria-selected='true']
.DocSearch-Hit-action, .DocSearch-Hit-icon, .DocSearch-Hit-path, .DocSearch-Hit-text, .DocSearch-Hit-title, .DocSearch-Hit-Tree, mark
color var(--bgcolor) !important
.DocSearch-ErrorScreen, .DocSearch-NoResults, .DocSearch-StartScreen
width 80%
margin 0 auto
padding 36px 0
font-size 0.9em
text-align center
.DocSearch-Screen-Icon
padding-bottom 12px
color var(--docsearch-muted-color)
.DocSearch-NoResults-Prefill-List
display inline-block
padding-bottom 24px
text-align left
ul
display inline-block
padding 8px 0 0
li
list-style-position inside
list-style-type '» '
.DocSearch-Prefill
appearance none
background none
border 0
border-radius 1em
color var(--accent-color)
cursor pointer
display inline-block
font-size 1em
font-weight 700
padding 0
&:focus, &:hover
outline none
text-decoration underline
.DocSearch-Footer
position relative
z-index 300
width 100%
height var(--docsearch-footer-height)
padding 0 var(--docsearch-spacing)
border-radius 0 0 8px 8px
box-shadow var(--docsearch-footer-shadow)
background var(--bgcolor)
display flex
flex-direction row-reverse
flex-shrink 0
align-items center
justify-content space-between
user-select none
@media (max-width $MQNarrow)
position absolute
bottom 0
border-radius 0
.DocSearch-Commands
margin 0
padding 0
color var(--docsearch-muted-color)
display flex
list-style none
@media (max-width $MQNarrow)
display none
li
display flex
align-items center
&:not(:last-of-type)
margin-right 0.8em
.DocSearch-Commands-Key
width 20px
height 18px
margin-right 0.4em
padding-bottom 2px
border-radius 2px
background var(--docsearch-key-gradient)
box-shadow var(--docsearch-key-shadow)
display flex
justify-content center
align-items center
</style>
import Vue from "vue";
import { SidebarHeader } from "@theme/utils/sidebar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {}, {}, {}, {
items: SidebarHeader[];
}>;
export default _default;
import Vue from "vue";
import { isActive } from "@theme/utils/path";
const renderLink = (h, { text, link, level }) => h("RouterLink", {
props: {
to: link,
activeClass: "",
exactActiveClass: "",
},
class: {
"anchor-link": true,
[level ? `heading${level}` : ""]: level,
},
}, [h("div", {}, [text])]);
const renderChildren = (h, { children, route }) => h("ul", { class: "anchor-list" }, children.map((child) => {
const active = isActive(route, `${route.path}#${child.slug}`);
return h("li", { class: { anchor: true, active } }, [
renderLink(h, {
text: child.title,
link: `${route.path}#${child.slug}`,
level: child.level,
}),
]);
}));
export default Vue.extend({
name: "Anchor",
functional: true,
props: {
items: {
type: Array,
default: () => [],
},
},
render(h, { props, parent: { $page, $route } }) {
return h("div", { attrs: { class: "anchor-place-holder" } }, [
h("aside", { attrs: { id: "anchor" } }, [
($page.headers && $page.headers.length)
? h("div", { class: "anchor-header" }, [
"On this page"
])
: null,
h("div", { class: "anchor-wrapper" }, [
props.items.length
? renderChildren(h, {
children: props.items,
route: $route,
})
: $page.headers
? renderChildren(h, {
children: $page.headers,
route: $route,
})
: null,
]),
($page.headers && $page.headers.length)
? h("div", [
h("div", { class: "anchor-header anchor-support" }, [
"Support"
]),
h("div", { class: "anchor-support-links" }, [
h("a", { attrs: { href: "https://discord.optimism.io", target: "_blank" } }, [
h("div", [
h("i", { attrs: { class: "fab fa-discord" } }),
" Discord community"
])
]),
h("a", { attrs: { href: "https://forms.monday.com/forms/c867f3f357707ff1fb4af0d3d5080710?r=use1", target: "_blank" } }, [
h("div", [
h("i", { attrs: { class: "far fa-comment-dots" } }),
" Get support for going live"
])
]),
h("a", { attrs: { href: "https://github.com/ethereum-optimism/optimism/issues", target: "_blank" } }, [
h("div", [
h("i", { attrs: { class: "fab fa-github" } }),
" Make an issue on GitHub"
])
]),
h("a", { attrs: { href: "https://github.com/ethereum-optimism/optimism/contribute", target: "_blank" } }, [
h("div", [
h("i", { attrs: { class: "far fa-hands-helping" } }),
" Contribute to Optimism"
])
]),
])
])
: null
]),
]);
},
});
//# sourceMappingURL=Anchor.js.map
{"version":3,"file":"Anchor.js","sourceRoot":"","sources":["Anchor.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAW7C,MAAM,UAAU,GAAG,CACjB,CAAgB,EAChB,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAc,EAC1B,EAAE,CACT,CAAC,CACC,YAAY,EACZ;IACE,KAAK,EAAE;QACL,EAAE,EAAE,IAAI;QACR,WAAW,EAAE,EAAE;QACf,gBAAgB,EAAE,EAAE;KACrB;IACD,KAAK,EAAE;QACL,aAAa,EAAE,IAAI;QACnB,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK;KACxC;CACF,EACD,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CACvB,CAAC;AAOJ,MAAM,cAAc,GAAG,CACrB,CAAgB,EAChB,EAAE,QAAQ,EAAE,KAAK,EAAyB,EACnC,EAAE,CACT,CAAC,CACC,IAAI,EACJ,EAAE,KAAK,EAAE,aAAa,EAAE,EACxB,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAoB,EAAE,EAAE;IACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAE9D,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE;QAClD,UAAU,CAAC,CAAC,EAAE;YACZ,IAAI,EAAE,KAAK,CAAC,KAAK;YACjB,IAAI,EAAE,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE;YACnC,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC;KACH,CAAC,CAAC;AACL,CAAC,CAAC,CACH,CAAC;AAEJ,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,QAAQ;IAEd,UAAU,EAAE,IAAI;IAEhB,KAAK,EAAE;QACL,KAAK,EAAE;YACL,IAAI,EAAE,KAAkC;YACxC,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;SAClB;KACF;IAED,MAAM,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAC5C,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,EAAE;YAC3D,CAAC,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;gBACtC,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,EAAE;oBACpC,KAAK,CAAC,KAAK,CAAC,MAAM;wBAChB,CAAC,CAAC,cAAc,CAAC,CAAC,EAAE;4BAChB,QAAQ,EAAE,KAAK,CAAC,KAAK;4BACrB,KAAK,EAAE,MAAM;yBACd,CAAC;wBACJ,CAAC,CAAC,KAAK,CAAC,OAAO;4BACf,CAAC,CAAC,cAAc,CAAC,CAAC,EAAE;gCAChB,QAAQ,EAAE,KAAK,CAAC,OAAO;gCACvB,KAAK,EAAE,MAAM;6BACd,CAAC;4BACJ,CAAC,CAAC,IAAI;iBACT,CAAC;aACH,CAAC;SACH,CAAC,CAAC;IACL,CAAC;CACF,CAAC,CAAC"}
\ No newline at end of file
<script src="./Anchor" />
<style lang="stylus">
$headings = 2 3 4 5 6
.anchor-place-holder
position sticky
top: ($navbarHeight + 2rem)
max-width $contentWidth
margin 0 auto
padding 0 2.5rem
z-index 99
@media (max-width $MQNarrow)
padding 0 1.5rem
& + .theme-default-content:not(.custom)
padding-top 0
#anchor
display none
position absolute
left calc(100% + 0.5rem)
min-width 10rem
max-width 15rem
max-height 85vh
overflow-y scroll
@media (min-width $MQWide)
.has-anchor &
display block
&::-webkit-scrollbar-track-piece
background transparent
&::-webkit-scrollbar
width 3px
&::-webkit-scrollbar-thumb:vertical
background #ddd
.theme-dark &
background #333
.anchor-wrapper
position relative
padding-left 8px
&::before
content ' '
position absolute
top 0
left 0px
bottom 0
width 2px
background var(--border-color)
z-index -1
> .anchor-list
margin 0
.anchor-list
padding-left 0
.anchor
position relative
box-sizing border-box
padding 1px 8px
list-style none
line-height 1.5
&::before
content ' '
position absolute
z-index 2
top 0
bottom 0
left -8px
width 2px
background transparent
&:hover
.anchor-link
color var(--accent-color)
&.active
.anchor-link
color var(--accent-color)
&::before
background var(--accent-color)
.anchor-link
display inline-block
vertical-align middle
position relative
max-width 100%
color var(--light-grey)
> div
text-overflow ellipsis
white-space nowrap
overflow hidden
for $heading in $headings
&.heading{$heading}
padding-left ($heading * 8 - 16) px
font-size: (16 - $heading)px
</style>
import Vue from "vue";
import type { PageComputed } from "@mr-hope/vuepress-types";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
author: string;
time: string;
tags: string[];
readingTimeContent: string;
readingTime: string;
authorText: string;
timeText: string;
tagText: string;
readingTimeText: string;
}, {
article: PageComputed;
}>;
export default _default;
import Vue from "vue";
import { capitalize } from "@mr-hope/vuepress-shared";
import AuthorIcon from "@mr-hope/vuepress-plugin-comment/lib/client/icons/AuthorIcon.vue";
import CalendarIcon from "@mr-hope/vuepress-plugin-comment/lib/client/icons/CalendarIcon.vue";
import CategoryInfo from "@mr-hope/vuepress-plugin-comment/lib/client/CategoryInfo.vue";
import TagInfo from "@mr-hope/vuepress-plugin-comment/lib/client/TagInfo.vue";
import TimerIcon from "@mr-hope/vuepress-plugin-comment/lib/client/icons/TimerIcon.vue";
export default Vue.extend({
name: "ArticleInfo",
components: {
AuthorIcon,
CalendarIcon,
CategoryInfo,
TagInfo,
TimerIcon,
},
props: {
article: { type: Object, required: true },
},
computed: {
author() {
return (this.article.frontmatter.author ||
(this.$themeConfig.author && this.article.frontmatter.author !== false
? this.$themeConfig.author
: ""));
},
time() {
const { date, time = date } = this.article.frontmatter;
if (typeof time === "string") {
if (time.indexOf("T") !== -1) {
const [dateString, temp] = time.split("T");
const [times] = temp.split(".");
return `${dateString} ${times === "00:00:00" ? "" : times}`;
}
return time;
}
return this.article.createTime || "";
},
tags() {
const { tag, tags = tag } = this.article.frontmatter;
if (typeof tags === "string")
return [capitalize(tags)];
if (Array.isArray(tags))
return tags.map((item) => capitalize(item));
return [];
},
readingTimeContent() {
return `PT${Math.max(Math.round(this.$page.readingTime.minutes), 1)}M`;
},
readingTime() {
const { minute, time } = READING_TIME_I18N[this.$localePath || "/"];
return this.article.readingTime.minutes < 1
? minute
: time.replace("$time", Math.round(this.article.readingTime.minutes).toString());
},
authorText() {
return PAGE_INFO_I18N[this.$localePath || "/"].author;
},
timeText() {
return PAGE_INFO_I18N[this.$localePath || "/"].time;
},
tagText() {
return PAGE_INFO_I18N[this.$localePath || "/"].tag;
},
readingTimeText() {
return PAGE_INFO_I18N[this.$localePath || "/"].readingTime;
},
},
});
//# sourceMappingURL=ArticleInfo.js.map
\ No newline at end of file
{"version":3,"file":"ArticleInfo.js","sourceRoot":"","sources":["ArticleInfo.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<template>
<div v-if="author || time" class="article-info">
<!-- Author -->
<span v-if="author" :aria-label="authorText" data-balloon-pos="down">
<AuthorIcon />
<span property="author" v-text="author" />
</span>
<!-- Writing Date -->
<span
v-if="time"
class="time"
:aria-label="timeText"
data-balloon-pos="down"
>
<CalendarIcon />
<span property="datePublished" v-text="time" />
</span>
<CategoryInfo
v-if="article.frontmatter.category"
:category="article.frontmatter.category"
/>
<TagInfo v-if="tags.length !== 0" :tags="tags" />
<!-- Reading time -->
<span
v-if="readingTime"
class="read-time-info"
:aria-label="readingTimeText"
data-balloon-pos="down"
>
<TimerIcon />
<span v-text="readingTime" />
<meta property="timeRequired" :content="readingTimeContent" />
</span>
</div>
</template>
<script src="./ArticleInfo" />
<style lang="stylus">
$articleInfoTextSize ?= 14px
.article-info
color var(--dark-grey)
font-size $articleInfoTextSize
font-family Arial, Helvetica, sans-serif
& > span
display inline-block
margin-right 0.5em
line-height 1.8
@media (max-width $MQMobileNarrow)
margin-right 0.3em
font-size 0.86rem
&::after
--balloon-font-size 8px
padding 0.3em 0.6em !important
svg
position relative
bottom -0.125em
.tags-wrapper
display inline-block
.icon
width 1em
height 1em
</style>
import Vue from "vue";
import type { PageComputed } from "@mr-hope/vuepress-types";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
isEncrypted: boolean;
excerpt: string;
}, {
article: PageComputed;
}>;
export default _default;
import Vue from "vue";
import ArticleInfo from "@theme/components/Blog/ArticleInfo.vue";
import LockIcon from "@theme/icons/LockIcon.vue";
import PresentationIcon from "@theme/icons/PresentationIcon.vue";
import StickyIcon from "@theme/icons/StickyIcon.vue";
import { getPathMatchedKeys } from "@theme/utils/encrypt";
export default Vue.extend({
name: "ArticleItem",
components: { ArticleInfo, LockIcon, StickyIcon, PresentationIcon },
props: {
article: { type: Object, required: true },
},
computed: {
isEncrypted() {
return (getPathMatchedKeys(this.$themeConfig.encrypt, this.article.path)
.length !== 0 || Boolean(this.article.frontmatter.password));
},
excerpt() {
if (this.article.excerpt)
return this.article.excerpt;
return (this.article.frontmatter.description ||
this.article.frontmatter.summary ||
"");
},
},
});
//# sourceMappingURL=ArticleItem.js.map
\ No newline at end of file
{"version":3,"file":"ArticleItem.js","sourceRoot":"","sources":["ArticleItem.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,WAAW,MAAM,wCAAwC,CAAC;AACjE,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AACjD,OAAO,gBAAgB,MAAM,mCAAmC,CAAC;AACjE,OAAO,UAAU,MAAM,6BAA6B,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAK1D,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,aAAa;IAEnB,UAAU,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE;IAEnE,KAAK,EAAE;QACL,OAAO,EAAE,EAAE,IAAI,EAAE,MAAgC,EAAE,QAAQ,EAAE,IAAI,EAAE;KACpE;IAED,QAAQ,EAAE;QACR,WAAW;YACT,OAAO,CACL,kBAAkB,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;iBAC7D,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAC9D,CAAC;QACJ,CAAC;QAED,OAAO;YACL,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO;gBAAE,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;YAEtD,OAAO,CACL,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW;gBACpC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO;gBAChC,EAAE,CACH,CAAC;QACJ,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<article class="article" vocab="https://schema.org/" typeof="Article">
<StickyIcon v-if="article.frontmatter.sticky" />
<header class="title" @click="$router.push(article.path)">
<LockIcon v-if="isEncrypted" />
<PresentationIcon v-if="article.frontmatter.layout === 'Slide'" />
<span property="headline">{{ article.title }}</span>
<meta
v-if="article.frontmatter.image"
property="image"
:content="$withBase(article.frontmatter.image)"
/>
</header>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="excerpt" class="excerpt" v-html="excerpt" />
<hr class="hr" />
<ArticleInfo :article="article" />
</article>
</template>
<script src="./ArticleItem" />
<style lang="stylus">
.article
position relative
box-sizing border-box
width 100%
margin 0 auto 20px
padding 16px 20px
background var(--bgcolor)
border-radius 6px
text-align left
box-shadow 0 1px 3px 0 var(--card-shadow-color)
@media (max-width $MQMobileNarrow)
border-radius 0
&:last-child
margin-bottom 0
&:hover
box-shadow 0 2px 6px 0 var(--card-shadow-color)
.sticky-icon
position absolute
top 0
right 0
width 40px
height 40px
fill var(--accent-color)
.sticky-text
fill var(--white)
.title
display inline-block
position relative
font-size 1.28rem
line-height 36px
&::after
content ''
position absolute
width 100%
height 2px
bottom 0
left 0
background var(--accent-color)
visibility hidden
transform scaleX(0)
transition transform 0.3s ease-in-out
&:hover
cursor pointer
&::after
visibility visible
transform scaleX(1)
.lock-icon, .presentation-icon
position relative
bottom -0.125em
display inline-block
vertical-align baseline
width 20px
height 20px
color var(--accent-color)
.excerpt
overflow hidden
line-height 1.7
h1
display none
& + p
margin-top 0.5em
p
&:first-child
margin-top 0.5em
&:last-child
margin-bottom 0.5em
// code block fix
pre
line-height 1.4
padding 1.25rem 1.5rem
margin 0.85rem 0
// line number fix
.line-numbers-mode
pre
padding-left ($lineNumbersWrapperWidth + 1) rem
// hide code demo
.code-demo-wrapper
display none
</style>
import Vue from "vue";
import type { BlogOptions } from "@theme/types";
import type { PageComputed } from "@mr-hope/vuepress-types";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
currentPage: number;
articleList: PageComputed[];
}, {
getArticleList(): PageComputed[];
}, {
blogConfig: BlogOptions;
articlePerPage: number;
filter: ((page: PageComputed) => boolean) | undefined;
$articles: PageComputed[];
articles: PageComputed[];
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import ArticleItem from "@theme/components/Blog/ArticleItem.vue";
import EmptyIcon from "@theme/icons/EmptyIcon.vue";
import MyTransition from "@theme/components/MyTransition.vue";
import { filterArticle, sortArticle } from "@theme/utils/article";
import { getPathMatchedKeys } from "@theme/utils/encrypt";
export default Vue.extend({
name: "ArticleList",
components: { ArticleItem, EmptyIcon, MyTransition },
data: () => ({
currentPage: 1,
articleList: [],
}),
computed: {
blogConfig() {
return this.$themeConfig.blog || {};
},
articlePerPage() {
return this.blogConfig.perPage || 10;
},
filter() {
const { path } = this.$route;
return path.includes("/article")
? (page) => page.frontmatter.layout !== "Slide"
: path.includes("/star")
? (page) => Boolean(page.frontmatter.star || page.frontmatter.sticky)
: path.includes("/encrypt")
? (page) => getPathMatchedKeys(this.$themeConfig.encrypt, page.path).length !==
0 || Boolean(page.frontmatter.password)
: path.includes("/slide")
? (page) => page.frontmatter.layout === "Slide"
: undefined;
},
$articles() {
// filter then sort
return sortArticle(filterArticle(this.$site.pages, this.filter), "sticky");
},
/** Articles in this page */
articles() {
return this.articleList.slice((this.currentPage - 1) * this.articlePerPage, this.currentPage * this.articlePerPage);
},
},
watch: {
// update article list when route is changed
$route(to, from) {
if (to.path !== from.path) {
this.articleList = this.getArticleList();
// reset page to 1
this.currentPage = 1;
}
},
currentPage() {
// list top border distance
const distance = document.querySelector("#article-list").getBoundingClientRect().top + window.scrollY;
setTimeout(() => {
window.scrollTo(0, distance);
}, 100);
},
},
mounted() {
this.articleList = this.getArticleList();
},
methods: {
getArticleList() {
try {
return this.$pagination
? // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
this.$pagination._matchedPages
: this.$articles;
}
catch (err) {
return this.$articles;
}
},
},
});
//# sourceMappingURL=ArticleList.js.map
\ No newline at end of file
{"version":3,"file":"ArticleList.js","sourceRoot":"","sources":["ArticleList.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<template>
<div id="article-list" class="article-wrapper">
<EmptyIcon v-if="!articles.length" class="empty" />
<MyTransition
v-for="(article, index) in articles"
:key="article.path"
:delay="index * 0.04"
>
<ArticleItem :article="article" />
</MyTransition>
<Pagination
v-model="currentPage"
:per-page="articlePerPage"
:total="articleList.length"
/>
</div>
</template>
<script src="./ArticleList" />
<style lang="stylus">
.article-wrapper
margin-top -0.5rem - $navbarHeight
padding-top: $navbarHeight + 0.5rem
text-align center
@media (max-width $MQMobile)
margin-top -0.5rem - $navbarMobileHeight
padding-top: $navbarMobileHeight + 0.5rem
.empty
max-width 560px
margin 0 auto
text-align center
</style>
import Vue from "vue";
interface ArticleTypeItem {
text: string;
path: string;
}
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
navigate(path: string): void;
}, {
types: ArticleTypeItem[];
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import { getDefaultLocale } from "@mr-hope/vuepress-shared";
import { navigate } from "@theme/utils/navigate";
export default Vue.extend({
name: "ArticleType",
computed: {
types() {
const blogI18n = this.$themeLocaleConfig.blog || getDefaultLocale().blog;
return [
{ text: blogI18n.allText, path: "/article/" },
{ text: blogI18n.star, path: "/star/" },
{ text: blogI18n.slides, path: "/slide/" },
{ text: blogI18n.encrypt, path: "/encrypt/" },
];
},
},
methods: {
navigate(path) {
navigate(path, this.$router, this.$route);
},
},
});
//# sourceMappingURL=ArticleType.js.map
\ No newline at end of file
{"version":3,"file":"ArticleType.js","sourceRoot":"","sources":["ArticleType.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAOjD,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,aAAa;IAEnB,QAAQ,EAAE;QACR,KAAK;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,IAAI,gBAAgB,EAAE,CAAC,IAAI,CAAC;YAEzE,OAAO;gBACL,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE;gBAC7C,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACvC,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;gBAC1C,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE;aAC9C,CAAC;QACJ,CAAC;KACF;IAED,OAAO,EAAE;QACP,QAAQ,CAAC,IAAY;YACnB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<ul class="article-type-wrapper">
<li
v-for="type in types"
:key="type.text"
class="article-type"
:class="{ active: type.path === $route.path }"
role="navigation"
@click="navigate(type.path)"
>
<span>{{ type.text }}</span>
</li>
</ul>
</template>
<script src="./ArticleType" />
<style lang="stylus">
.article-type-wrapper
position relative
padding-left 0
font-size 18px
font-family Arial, Helvetica, sans-serif
font-weight 600
display flex
justify-content center
align-items center
list-style none
z-index 2
@media (max-width $MQMobileNarrow)
font-size 16px
.article-type
position relative
vertical-align middle
margin 0.3em 0.8em
line-height 1.2
cursor pointer
&::after
position absolute
content ' '
left 50%
right 50%
bottom -6px
height 2px
background var(--accent-color)
border-radius 1px
visibility hidden
transition left 0.2s ease-in-out, right 0.2s ease-in-out
span
transition all 0.3s ease-in-out
&.active
position relative
span
display inline-block
color var(--accent-color)
transform scale(1.1, 1.1)
&:hover, &.active
&::after
left calc(50% - 8px)
right calc(50% - 8px)
visibility visible
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
defaultHeroImage: string;
}, unknown, {
heroImageStyle: Record<string, string>;
bgImageStyle: Record<string, string>;
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import MyTransition from "@theme/components/MyTransition.vue";
import defaultHeroImage from "@theme/assets/hero.jpg";
export default Vue.extend({
name: "BlogHero",
components: { MyTransition },
data: () => ({ defaultHeroImage }),
computed: {
heroImageStyle() {
const defaultStyle = {
maxHeight: "180px",
margin: this.$frontmatter.showTitle === false
? "6rem auto 1.5rem"
: "1rem auto",
};
return Object.assign(Object.assign({}, defaultStyle), this.$frontmatter.heroImageStyle);
},
bgImageStyle() {
const defaultBgImageStyle = {
height: "350px",
textAlign: "center",
overflow: "hidden",
};
const { bgImageStyle = {} } = this.$frontmatter;
return Object.assign(Object.assign({}, defaultBgImageStyle), bgImageStyle);
},
},
});
//# sourceMappingURL=BlogHero.js.map
\ No newline at end of file
{"version":3,"file":"BlogHero.js","sourceRoot":"","sources":["BlogHero.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAC9D,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AAEtD,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU;IAEhB,UAAU,EAAE,EAAE,YAAY,EAAE;IAE5B,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAElC,QAAQ,EAAE;QACR,cAAc;YACZ,MAAM,YAAY,GAAG;gBACnB,SAAS,EAAE,OAAO;gBAClB,MAAM,EACJ,IAAI,CAAC,YAAY,CAAC,SAAS,KAAK,KAAK;oBACnC,CAAC,CAAC,kBAAkB;oBACpB,CAAC,CAAC,WAAW;aAClB,CAAC;YAEF,uCACK,YAAY,GACX,IAAI,CAAC,YAAY,CAAC,cAAyC,EAC/D;QACJ,CAAC;QAED,YAAY;YACV,MAAM,mBAAmB,GAA2B;gBAClD,MAAM,EAAE,OAAO;gBACf,SAAS,EAAE,QAAQ;gBACnB,QAAQ,EAAE,QAAQ;aACnB,CAAC;YACF,MAAM,EAAE,YAAY,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;YAEhD,uCACK,mBAAmB,GAClB,YAAuC,EAC3C;QACJ,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div
v-if="$frontmatter.hero !== false"
class="blog-hero"
:class="{ full: $frontmatter.heroFullScreen }"
:style="{ ...bgImageStyle }"
>
<div
class="mask"
:style="{
background: `url(${
$frontmatter.bgImage
? $withBase($frontmatter.bgImage)
: defaultHeroImage
}) center/cover no-repeat`,
}"
/>
<MyTransition :delay="0.04">
<img
v-if="$frontmatter.heroImage"
class="hero-logo"
:style="heroImageStyle || {}"
:src="$withBase($frontmatter.heroImage)"
alt="hero"
/>
</MyTransition>
<MyTransition :delay="0.08">
<h1 v-if="$frontmatter.showTitle !== false">
{{ $frontmatter.heroText || $title || "Hope" }}
</h1>
</MyTransition>
<MyTransition :delay="0.12">
<p v-if="$description" class="description" v-text="$description" />
</MyTransition>
</div>
</template>
<script src="./BlogHero" />
<style lang="stylus">
.blog-hero
position relative
color #eee
margin-bottom 16px
height 450px
display flex
flex-direction column
justify-content center
@media (max-width $MQMobile)
height 350px
margin 0 -1.5rem 16px
@media (max-width $MQMobileNarrow)
margin 0 0 16px
&.full
height 'calc(100vh - %s)' % $navbarHeight !important
@media (max-width $MQMobile)
height 'calc(100vh - %s)' % $navbarMobileHeight !important
.mask
background-position-y top !important
.mask
position absolute
top 0
bottom 0
left 0
right 0
&:after
display block
content ' '
background var(--light-grey)
position absolute
top 0
bottom 0
left 0
right 0
z-index 1
opacity 0.2
& > :not(.mask)
position relative
z-index 2
h1
margin 0.5rem auto
font-size 36px
@media (max-width $MQNarrow)
font-size 30px
@media (max-width $MQMobile)
font-size 36px
@media (max-width $MQMobileNarrow)
font-size 30px
.hero-logo + h1
margin 0 auto
.description
margin 1.2rem auto 0
font-size 20px
@media (max-width $MQNarrow)
font-size 18px
@media (max-width $MQMobile)
font-size 20px
@media (max-width $MQMobileNarrow)
font-size 18px
</style>
import Vue from "vue";
/**
* 项目配置
*
* Project Configuration
*/
export interface ProjectOptions {
/**
* 项目类型
*
* Type of project
*/
type: "article" | "book" | "link" | "project";
/**
* 项目名称
*
* Project name
*/
name: string;
/**
* 项目描述
*
* Project desription
*/
desc?: string;
/**
* 项目封面,应为绝对路径
*
* Cover for the project, must be an absolute path
*/
cover?: string;
/**
* 项目链接
*
* Link of the project
*/
link: string;
}
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, unknown, Record<never, any>>;
export default _default;
import Vue from "vue";
import ArticleList from "@theme/components/Blog/ArticleList.vue";
import BlogHero from "@theme/components/Blog/BlogHero.vue";
import BlogInfo from "@BlogInfo";
import MyTransition from "@theme/components/MyTransition.vue";
import ProjectList from "@theme/components/Blog/ProjectList.vue";
export default Vue.extend({
name: "BlogHome",
components: {
ArticleList,
BlogHero,
BlogInfo,
MyTransition,
ProjectList,
},
});
//# sourceMappingURL=BlogHome.js.map
\ No newline at end of file
{"version":3,"file":"BlogHome.js","sourceRoot":"","sources":["BlogHome.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,WAAW,MAAM,wCAAwC,CAAC;AACjE,OAAO,QAAQ,MAAM,qCAAqC,CAAC;AAC3D,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAC9D,OAAO,WAAW,MAAM,wCAAwC,CAAC;AAwCjE,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU;IAEhB,UAAU,EAAE;QACV,WAAW;QACX,QAAQ;QACR,QAAQ;QACR,YAAY;QACZ,WAAW;KACZ;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="page blog">
<BlogHero />
<div class="blog-page-wrapper">
<main class="blog-home">
<MyTransition :delay="0.16">
<ProjectList />
</MyTransition>
<MyTransition :delay="0.24">
<ArticleList :key="$route.path" />
</MyTransition>
</main>
<MyTransition :delay="0.16">
<BlogInfo />
</MyTransition>
</div>
<MyTransition :delay="0.28">
<Content :key="$route.path" class="theme-default-content" custom />
</MyTransition>
</div>
</template>
<script src="./BlogHome" />
<style lang="stylus">
.page.blog
box-sizing content-box
min-height 100vh
padding-top $navbarHeight
padding-bottom 2rem
margin 0px auto
background var(--bgcolor-light)
@media (max-width $MQMobile)
padding $navbarMobileHeight 1.5rem 2rem
@media (max-width $MQMobileNarrow)
padding-left 0
padding-right 0
.blog-page-wrapper
display flex
justify-content center
align-items flex-start
margin 0 auto
@media (min-width $MQMobile)
padding 0 1rem
@media (min-width $MQNarrow)
padding 0 2rem
@media (min-width $MQWide)
padding 0
.blog-home
max-width 780px
overflow hidden
flex 1
@media (min-width $MQMobile)
margin 0 15px
.theme-default-content:empty
padding 0
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, unknown, Record<never, any>>;
export default _default;
import Vue from "vue";
import BlogInfoList from "@theme/components/Blog/BlogInfoList.vue";
import BloggerInfo from "@theme/components/Blog/BloggerInfo.vue";
import MyTransition from "@theme/components/MyTransition.vue";
export default Vue.extend({
name: "BlogInfo",
components: { BlogInfoList, BloggerInfo, MyTransition },
});
//# sourceMappingURL=BlogInfo.js.map
\ No newline at end of file
{"version":3,"file":"BlogInfo.js","sourceRoot":"","sources":["BlogInfo.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,YAAY,MAAM,yCAAyC,CAAC;AACnE,OAAO,WAAW,MAAM,wCAAwC,CAAC;AACjE,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAE9D,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU;IAEhB,UAAU,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE;CACxD,CAAC,CAAC"}
\ No newline at end of file
<template>
<aside class="blog-info-wrapper">
<MyTransition>
<BloggerInfo />
</MyTransition>
<MyTransition :delay="0.04">
<BlogInfoList />
</MyTransition>
</aside>
</template>
<script src="./BlogInfo" />
<style lang="stylus">
.blog-info-wrapper
.sidebar &
.blogger-info
display none
.page &
position sticky
box-sizing border-box
top ($navbarHeight + 1rem)
flex 0 0 300px
height auto
margin-bottom 12px
transition all 0.3s
@media (max-width $MQMobile)
display none
.blogger-info
margin-bottom 16px
padding 8px 0
border-radius 8px
box-shadow 0 1px 3px 0 var(--card-shadow-color)
&:hover
box-shadow 0 2px 6px 0 var(--card-shadow-color)
</style>
import ArticleIcon from "@theme/icons/ArticleIcon.vue";
declare const _default: import("vue/types/vue").ExtendedVue<{
$starArticles: import("@mr-hope/vuepress-types").PageComputed[];
} & Record<never, any> & ArticleIcon, {
active: string;
}, {
setActive(name: string): void;
navigate(path: string): void;
}, {
i18n: {
article: string;
articleList: string;
category: string;
tag: string;
timeline: string;
timelineText: string;
allText: string;
intro: string;
star: string;
slides: string;
encrypt: string;
};
articleNumber: number;
}, Record<never, any>>;
export default _default;
import { getDefaultLocale } from "@mr-hope/vuepress-shared";
import ArticleIcon from "@theme/icons/ArticleIcon.vue";
import CategoryIcon from "@mr-hope/vuepress-plugin-comment/lib/client/icons/CategoryIcon.vue";
import TagIcon from "@mr-hope/vuepress-plugin-comment/lib/client/icons/TagIcon.vue";
import TimeIcon from "@mr-hope/vuepress-plugin-comment/lib/client/icons/TimeIcon.vue";
import ArticleList from "@theme/components/Blog/ArticleList.vue";
import CategoryList from "@theme/components/Blog/CategoryList.vue";
import MyTransition from "@theme/components/MyTransition.vue";
import TagList from "@theme/components/Blog/TagList.vue";
import Timeline from "@theme/components/Blog/Timeline.vue";
import TimelineList from "@theme/components/Blog/TimelineList.vue";
import { filterArticle } from "@theme/utils/article";
import { starMixin } from "@theme/mixins/star";
export default starMixin.extend({
name: "BlogInfo",
components: {
ArticleIcon,
ArticleList,
CategoryIcon,
CategoryList,
MyTransition,
TagIcon,
TagList,
TimeIcon,
Timeline,
TimelineList,
},
data: () => ({
active: "category",
}),
computed: {
i18n() {
return this.$themeLocaleConfig.blog || getDefaultLocale().blog;
},
articleNumber() {
return filterArticle(this.$site.pages).length;
},
},
methods: {
setActive(name) {
this.active = name;
},
navigate(path) {
if (this.$route.path !== path)
void this.$router.push(path);
},
},
});
//# sourceMappingURL=BlogInfoList.js.map
\ No newline at end of file
{"version":3,"file":"BlogInfoList.js","sourceRoot":"","sources":["BlogInfoList.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,WAAW,MAAM,8BAA8B,CAAC;AACvD,OAAO,YAAY,MAAM,oEAAoE,CAAC;AAC9F,OAAO,OAAO,MAAM,+DAA+D,CAAC;AACpF,OAAO,QAAQ,MAAM,gEAAgE,CAAC;AACtF,OAAO,WAAW,MAAM,wCAAwC,CAAC;AACjE,OAAO,YAAY,MAAM,yCAAyC,CAAC;AACnE,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAC9D,OAAO,OAAO,MAAM,oCAAoC,CAAC;AACzD,OAAO,QAAQ,MAAM,qCAAqC,CAAC;AAC3D,OAAO,YAAY,MAAM,yCAAyC,CAAC;AACnE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAI/C,eAAe,SAAS,CAAC,MAAM,CAAC;IAC9B,IAAI,EAAE,UAAU;IAEhB,UAAU,EAAE;QACV,WAAW;QACX,WAAW;QACX,YAAY;QACZ,YAAY;QACZ,YAAY;QACZ,OAAO;QACP,OAAO;QACP,QAAQ;QACR,QAAQ;QACR,YAAY;KACb;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,MAAM,EAAE,UAAU;KACnB,CAAC;IAEF,QAAQ,EAAE;QACR,IAAI;YACF,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,IAAI,gBAAgB,EAAE,CAAC,IAAI,CAAC;QACjE,CAAC;QAED,aAAa;YACX,OAAO,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;QAChD,CAAC;KACF;IAED,OAAO,EAAE;QACP,SAAS,CAAC,IAAY;YACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,CAAC;QAED,QAAQ,CAAC,IAAY;YACnB,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI;gBAAE,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="blog-info-list">
<div class="switch-wrapper">
<button class="switch-button" @click="setActive('article')">
<div
class="icon-wapper"
:class="{ active: active === 'article' }"
:aria-label="i18n.article"
data-balloon-pos="up"
>
<ArticleIcon />
</div>
</button>
<button class="switch-button" @click="setActive('category')">
<div
class="icon-wapper"
:class="{ active: active === 'category' }"
:aria-label="i18n.category"
data-balloon-pos="up"
>
<CategoryIcon />
</div>
</button>
<button class="switch-button" @click="setActive('tag')">
<div
class="icon-wapper"
:class="{ active: active === 'tag' }"
:aria-label="i18n.tag"
data-balloon-pos="up"
>
<TagIcon />
</div>
</button>
<button class="switch-button" @click="setActive('timeline')">
<div
class="icon-wapper"
:class="{ active: active === 'timeline' }"
:aria-label="i18n.timeline"
data-balloon-pos="up"
>
<TimeIcon />
</div>
</button>
</div>
<!-- Article -->
<MyTransition v-if="active === 'article'">
<div class="sticky-article-wrapper">
<div class="title" @click="navigate('/article/')">
<ArticleIcon />
<span class="num">{{ articleNumber }}</span>
{{ i18n.article }}
</div>
<hr />
<ul class="sticky-article-list">
<MyTransition
v-for="(article, index) in $starArticles"
:key="article.path"
:delay="(index + 1) * 0.08"
>
<li
class="sticky-article"
@click="navigate(article.path)"
v-text="article.title"
/>
</MyTransition>
</ul>
</div>
</MyTransition>
<!-- Category -->
<MyTransition v-if="active === 'category'">
<div class="category-wrapper">
<div
v-if="$category.list.length !== 0"
class="title"
@click="navigate('/category/')"
>
<CategoryIcon />
<span class="num">{{ $category.list.length }}</span>
{{ i18n.category }}
</div>
<hr />
<MyTransition :delay="0.04">
<CategoryList />
</MyTransition>
</div>
</MyTransition>
<!-- Tags -->
<MyTransition v-if="active === 'tag'">
<div class="tag-wrapper">
<div
v-if="$tag.list.length !== 0"
class="title"
@click="navigate('/tag/')"
>
<TagIcon />
<span class="num">{{ $tag.list.length }}</span>
{{ i18n.tag }}
</div>
<hr />
<MyTransition :delay="0.04">
<TagList />
</MyTransition>
</div>
</MyTransition>
<!-- Timeline -->
<MyTransition v-if="active === 'timeline'">
<TimelineList />
</MyTransition>
</div>
</template>
<script src="./BlogInfoList" />
<style lang="stylus">
@require '~@mr-hope/vuepress-shared/styles/reset'
.blog-info-list
margin 8px auto
padding 8px 16px
.page &
background var(--bgcolor)
border-radius 6px
box-shadow 0 1px 3px 0 var(--card-shadow-color)
&:hover
box-shadow 0 2px 6px 0 var(--card-shadow-color)
.switch-wrapper
display flex
justify-content center
margin-bottom 8px
.switch-button
button()
width 44px
height 44px
margin 0 8px
padding 4px
color var(--grey3)
&:focus
outline none
.icon-wapper
width 20px
height 20px
padding 8px
border-radius 50%
background rgba(127, 127, 127, 0.15)
.theme-dark &
background rgba(255, 255, 255, 0.15)
&:hover
cursor pointer
&.active
.theme-light &
background var(--accent-color-l10)
.theme-dark &
background var(--accent-color-d10)
.icon
width 100%
height 100%
.sticky-article-wrapper, .category-wrapper, .tag-wrapper
padding 8px 0
.title
cursor pointer
.icon
position relative
bottom -0.125rem
width 16px
height 16px
margin 0 6px
.num
position relative
margin 0 2px
font-size 22px
.sticky-article-wrapper
.sticky-article-list
margin 8px auto
.sticky-article
padding 12px 8px 4px
border-bottom 1px dashed var(--grey14)
&:hover
cursor pointer
color var(--accent-color)
.category-wrapper
.category-list-wrapper
margin 8px auto
.tag-wrapper
.tag-list-wrapper
margin 8px auto
.page &
.timeline-list-wrapper
.content
max-height 60vh
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
showArticles: boolean;
componentName: string;
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import ArticleList from "@theme/components/Blog/ArticleList.vue";
import ArticleType from "@theme/components/Blog/ArticleType.vue";
import BlogInfo from "@BlogInfo";
import CategoryList from "@theme/components/Blog/CategoryList.vue";
import MyTransition from "@theme/components/MyTransition.vue";
import TagList from "@theme/components/Blog/TagList.vue";
import Timeline from "@theme/components/Blog/Timeline.vue";
import TimelineList from "@theme/components/Blog/TimelineList.vue";
export default Vue.extend({
name: "BlogPage",
components: {
ArticleList,
ArticleType,
BlogInfo,
CategoryList,
MyTransition,
TagList,
Timeline,
TimelineList,
},
computed: {
showArticles() {
const { path } = this.$route;
return !path.includes("/timeline");
},
componentName() {
const pathName = this.$route.path.split("/")[1];
if (["category", "tag"].includes(pathName))
return `${pathName}List`;
else if (pathName === "timeline")
return pathName;
return "articleType";
},
},
});
//# sourceMappingURL=BlogPage.js.map
\ No newline at end of file
{"version":3,"file":"BlogPage.js","sourceRoot":"","sources":["BlogPage.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,WAAW,MAAM,wCAAwC,CAAC;AACjE,OAAO,WAAW,MAAM,wCAAwC,CAAC;AACjE,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,YAAY,MAAM,yCAAyC,CAAC;AACnE,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAC9D,OAAO,OAAO,MAAM,oCAAoC,CAAC;AACzD,OAAO,QAAQ,MAAM,qCAAqC,CAAC;AAC3D,OAAO,YAAY,MAAM,yCAAyC,CAAC;AAEnE,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU;IAEhB,UAAU,EAAE;QACV,WAAW;QACX,WAAW;QACX,QAAQ;QACR,YAAY;QACZ,YAAY;QACZ,OAAO;QACP,QAAQ;QACR,YAAY;KACb;IAED,QAAQ,EAAE;QACR,YAAY;YACV,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;YAE7B,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACrC,CAAC;QAED,aAAa;YACX,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAEhD,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;gBAAE,OAAO,GAAG,QAAQ,MAAM,CAAC;iBAChE,IAAI,QAAQ,KAAK,UAAU;gBAAE,OAAO,QAAQ,CAAC;YAElD,OAAO,aAAa,CAAC;QACvB,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<main class="blog-page">
<MyTransition>
<component :is="componentName" v-if="componentName" />
</MyTransition>
<MyTransition :delay="0.24">
<ArticleList v-if="showArticles" :key="$route.path" />
</MyTransition>
</main>
</template>
<script src="./BlogPage" />
<style lang="stylus">
.blog-page
max-width 780px
flex 1
@media (min-width $MQMobile)
margin 0 15px
.article-title
font-size 1.8rem
margin 10px 15px
</style>
import MediaLinks from "@theme/components/MediaLinks.vue";
import type { BlogOptions } from "@theme/types";
declare const _default: import("vue/types/vue").ExtendedVue<{
$timelineItems: import("@mr-hope/vuepress-types").PageComputed[];
$timeline: import("@theme/mixins/timeline").TimelineItem[];
} & Record<never, any> & MediaLinks, unknown, {
navigate(url: string): void;
jumpIntro(): void;
}, {
blogConfig: BlogOptions;
bloggerName: string;
bloggerAvatar: string;
hasIntro: boolean;
hintAttr: string;
i18n: {
article: string;
articleList: string;
category: string;
tag: string;
timeline: string;
timelineText: string;
allText: string;
intro: string;
star: string;
slides: string;
encrypt: string;
};
articleNumber: number;
}, Record<never, any>>;
export default _default;
import { getDefaultLocale } from "@mr-hope/vuepress-shared";
import MediaLinks from "@theme/components/MediaLinks.vue";
import { timelineMixin } from "@theme/mixins/timeline";
import { filterArticle } from "@theme/utils/article";
import { navigate } from "@theme/utils/navigate";
export default timelineMixin.extend({
name: "BloggerInfo",
components: { MediaLinks },
computed: {
blogConfig() {
return this.$themeConfig.blog || {};
},
bloggerName() {
return (this.blogConfig.name ||
this.$themeConfig.author ||
this.$site.title ||
"");
},
bloggerAvatar() {
return this.blogConfig.avatar || this.$themeConfig.logo || "";
},
hasIntro() {
return Boolean(this.blogConfig.intro);
},
hintAttr() {
return this.hasIntro ? "aria-label" : "";
},
i18n() {
return this.$themeLocaleConfig.blog || getDefaultLocale().blog;
},
articleNumber() {
return filterArticle(this.$site.pages).length;
},
},
methods: {
navigate(url) {
navigate(url, this.$router, this.$route);
},
jumpIntro() {
if (this.hasIntro)
navigate(this.blogConfig.intro, this.$router, this.$route);
},
},
});
//# sourceMappingURL=BloggerInfo.js.map
\ No newline at end of file
{"version":3,"file":"BloggerInfo.js","sourceRoot":"","sources":["BloggerInfo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,UAAU,MAAM,kCAAkC,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAKjD,eAAe,aAAa,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,aAAa;IAEnB,UAAU,EAAE,EAAE,UAAU,EAAE;IAE1B,QAAQ,EAAE;QACR,UAAU;YACR,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC;QACtC,CAAC;QAED,WAAW;YACT,OAAO,CACL,IAAI,CAAC,UAAU,CAAC,IAAI;gBACpB,IAAI,CAAC,YAAY,CAAC,MAAM;gBACxB,IAAI,CAAC,KAAK,CAAC,KAAK;gBAChB,EAAE,CACH,CAAC;QACJ,CAAC;QAED,aAAa;YACX,OAAO,IAAI,CAAC,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC;QAChE,CAAC;QAED,QAAQ;YACN,OAAO,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACxC,CAAC;QAED,QAAQ;YACN,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,CAAC;QAED,IAAI;YACF,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,IAAI,gBAAgB,EAAE,CAAC,IAAI,CAAC;QACjE,CAAC;QAED,aAAa;YACX,OAAO,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;QAChD,CAAC;KACF;IAED,OAAO,EAAE;QACP,QAAQ,CAAC,GAAW;YAClB,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC3C,CAAC;QAED,SAAS;YACP,IAAI,IAAI,CAAC,QAAQ;gBACf,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,KAAe,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACzE,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="blogger-info" vocab="https://schema.org/" typeof="Person">
<div
class="blogger"
:class="{ hasIntro }"
:[hintAttr]="hasIntro ? i18n.intro : ''"
:data-balloon-pos="hasIntro ? 'down' : ''"
role="navigation"
@click="jumpIntro"
>
<img
v-if="bloggerAvatar"
class="avatar"
:class="{ round: blogConfig.roundAvatar !== false }"
property="image"
alt="Blogger Avatar"
:src="$withBase(bloggerAvatar)"
/>
<div
v-if="bloggerName"
class="name"
property="name"
v-text="bloggerName"
/>
<meta
v-if="hasIntro"
property="url"
:content="$withBase(blogConfig.intro)"
/>
</div>
<div class="num-wrapper">
<div @click="navigate('/article/')">
<div class="num">{{ articleNumber }}</div>
<div>{{ i18n.article }}</div>
</div>
<div @click="navigate('/category/')">
<div class="num">{{ $category.list.length }}</div>
<div>{{ i18n.category }}</div>
</div>
<div @click="navigate('/tag/')">
<div class="num">{{ $tag.list.length }}</div>
<div>{{ i18n.tag }}</div>
</div>
<div @click="navigate('/timeline/')">
<div class="num">{{ $timelineItems.length }}</div>
<div>{{ i18n.timeline }}</div>
</div>
</div>
<MediaLinks />
</div>
</template>
<script src="./BloggerInfo" />
<style lang="stylus">
.blogger-info
.page &
background var(--bgcolor)
.blogger
padding 8px 0
text-align center
&.hasIntro
cursor pointer
.avatar
width 128px
height 128px
margin 0 auto
&.round
border-radius 50%
.name
margin 16px auto
font-size 22px
.num-wrapper
display flex
margin 0 auto 16px
width 80%
> div
width 25%
text-align center
font-size 13px
cursor pointer
&:hover
color var(--accent-color)
.num
position relative
margin-bottom 8px
font-weight 600
font-size 20px
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
capitalize: (word: string) => string;
clickCategory(path: string): void;
}, unknown, Record<never, any>>;
export default _default;
import Vue from "vue";
import { capitalize } from "@mr-hope/vuepress-shared";
import { navigate } from "@theme/utils/navigate";
export default Vue.extend({
name: "CategoryList",
methods: {
capitalize,
clickCategory(path) {
navigate(path, this.$router, this.$route);
},
},
});
//# sourceMappingURL=CategoryList.js.map
\ No newline at end of file
{"version":3,"file":"CategoryList.js","sourceRoot":"","sources":["CategoryList.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,cAAc;IAEpB,OAAO,EAAE;QACP,UAAU;QAEV,aAAa,CAAC,IAAY;YACxB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<ul class="category-list-wrapper">
<li
v-for="(category, index) in $category.list"
:key="category.path"
class="category"
:class="{
active: category.path === $route.path,
[`category${index % 9}`]: true,
}"
@click="clickCategory(category.path)"
>
{{ capitalize(category.name) }}
<span class="category-num">{{ category.pages.length }}</span>
</li>
</ul>
</template>
<script src="./CategoryList" />
<style lang="stylus">
$categoryListTextSize ?= 14px
.category-list-wrapper
position relative
z-index 2
padding-left 0
font-size $categoryListTextSize
font-family Arial, Helvetica, sans-serif
list-style none
.category
display inline-block
vertical-align middle
margin 0.3rem 0.6rem 0.8rem
padding 0.4rem 0.8rem
border-radius 0.25rem
box-shadow 0 1px 4px 0 var(--card-shadow-color)
color var(--dark-grey)
cursor pointer
overflow hidden
transition background-color 0.3s, color 0.3s
@media (max-width $MQMobileNarrow)
font-size 0.9rem
.category-num
display inline-block
min-width 1rem
height 1.2rem
margin-left 0.2em
padding 0 0.1rem
border-radius 0.6rem
color var(--white)
font-family sans-serif
font-size 0.7rem
line-height 1.2rem
text-align center
@require '~@mr-hope/vuepress-shared/styles/colors.styl'
for $color, $index in $colors
.category-list-wrapper .category{$index}
&, .theme-light &
background lighten($color, 90%)
&:hover
background lighten($color, 75%)
&.active
background var(--accent-color)
color var(--white)
.category-num
color var(--accent-color)
background var(--bgcolor-light)
.theme-dark &
background darken($color, 75%)
&:hover
background darken($color, 60%)
&.active
background var(--accent-color-d10)
color var(--white)
.category-num
background $color
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
navigate(link: string): void;
}, unknown, Record<never, any>>;
export default _default;
import Vue from "vue";
import ArticleIcon from "@theme/icons/ArticleIcon.vue";
import BookIcon from "@theme/icons/BookIcon.vue";
import LinkIcon from "@theme/icons/LinkIcon.vue";
import ProjectIcon from "@theme/icons/ProjectIcon.vue";
import { navigate } from "@theme/utils/navigate";
export default Vue.extend({
name: "ProjectList",
components: { ArticleIcon, BookIcon, LinkIcon, ProjectIcon },
methods: {
navigate(link) {
navigate(link, this.$router, this.$route);
},
},
});
//# sourceMappingURL=ProjectList.js.map
\ No newline at end of file
{"version":3,"file":"ProjectList.js","sourceRoot":"","sources":["ProjectList.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,WAAW,MAAM,8BAA8B,CAAC;AACvD,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AACjD,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AACjD,OAAO,WAAW,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,aAAa;IAEnB,UAAU,EAAE,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE;IAE5D,OAAO,EAAE;QACP,QAAQ,CAAC,IAAY;YACnB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="project-list">
<div
v-for="(project, index) in $frontmatter.project || []"
:key="project.name"
class="project"
:class="`project${index % 9}`"
@click="navigate(project.link)"
>
<div
v-if="project.cover"
class="cover"
:style="`background: url(${$withBase(
project.cover
)}) center/cover no-repeat;`"
/>
<component :is="`${project.type}-icon`" />
<div class="name">{{ project.name }}</div>
<div class="desc">{{ project.desc }}</div>
</div>
</div>
</template>
<script src="./ProjectList" />
<style lang="stylus">
.project-list
position relative
display flex
justify-content flex-start
align-content stretch
align-items stretch
flex-wrap wrap
font-family sans-serif
margin-bottom 12px
z-index 2
.project
position relative
width calc(50% - 40px)
background-color var(--grey14)
border-radius 8px
margin 6px 8px
padding 12px
transition background-color 0.3s, transform 0.3s
@media (min-width $MQNarrow)
width calc(33% - 40px)
@media (min-width $MQWide)
width calc(25% - 40px)
&:hover
cursor pointer
transform scale(0.98, 0.98)
.cover
content ''
opacity 0.5
top 0
left 0
bottom 0
right 0
position absolute
z-index 1
.icon
position relative
z-index 2
float right
width 20px
height 20px
.name
position relative
z-index 2
color var(--grey3)
font-size 16px
font-weight 500
.desc
position relative
z-index 2
margin 6px 0
color var(--dark-grey)
font-size 13px
@require '~@mr-hope/vuepress-shared/styles/colors.styl'
for $color, $index in $colors
.project-list .project{$index}
&, .theme-light &
background lighten($color, 90%)
&:hover
background lighten($color, 75%)
.theme-dark &
background darken($color, 75%)
&:hover
background darken($color, 60%)
</style>
import Vue from "vue";
interface TagOption {
name: string;
path: string;
}
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
isActive(name: string): boolean;
clickTag(path: string): void;
}, {
tagList: TagOption[];
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import { getDefaultLocale } from "@mr-hope/vuepress-shared";
import { navigate } from "@theme/utils/navigate";
export default Vue.extend({
name: "TagList",
computed: {
tagList() {
return [
{
name:
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.$themeLocaleConfig.blog.allText ||
getDefaultLocale().blog.allText,
path: "/tag/",
},
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
...this.$tag.list,
];
},
},
methods: {
isActive(name) {
return (name ===
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
((this.$currentTag && this.$currentTag.key) ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.$themeLocaleConfig.blog.allText ||
getDefaultLocale().blog.allText));
},
clickTag(path) {
navigate(path, this.$router, this.$route);
},
},
});
//# sourceMappingURL=TagList.js.map
\ No newline at end of file
{"version":3,"file":"TagList.js","sourceRoot":"","sources":["TagList.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAOjD,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,SAAS;IAEf,QAAQ,EAAE;QACR,OAAO;YACL,OAAO;gBACL;oBACE,IAAI;oBACF,oEAAoE;oBACpE,IAAI,CAAC,kBAAkB,CAAC,IAAK,CAAC,OAAO;wBACrC,gBAAgB,EAAE,CAAC,IAAI,CAAC,OAAO;oBACjC,IAAI,EAAE,OAAO;iBACd;gBACD,sEAAsE;gBACtE,GAAI,IAAI,CAAC,IAAI,CAAC,IAAoB;aACnC,CAAC;QACJ,CAAC;KACF;IAED,OAAO,EAAE;QACP,QAAQ,CAAC,IAAY;YACnB,OAAO,CACL,IAAI;gBACJ,sEAAsE;gBACtE,CAAC,CAAC,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC;oBACzC,oEAAoE;oBACpE,IAAI,CAAC,kBAAkB,CAAC,IAAK,CAAC,OAAO;oBACrC,gBAAgB,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CACnC,CAAC;QACJ,CAAC;QAED,QAAQ,CAAC,IAAY;YACnB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<ul class="tag-list-wrapper">
<li
v-for="(tag, index) in tagList"
:key="tag.path"
class="tag"
:class="{ active: isActive(tag.name), [`tag${index % 9}`]: true }"
@click="clickTag(tag.path)"
>
<div class="tag-name">{{ tag.name }}</div>
</li>
</ul>
</template>
<script src="./TagList" />
<style lang="stylus">
.tag-list-wrapper
position relative
z-index 2
padding-left 0
font-family Arial, Helvetica, sans-serif
list-style none
display flex
flex-wrap wrap
justify-content space-evenly
.tag
display inline-block
position relative
vertical-align middle
min-width 24px
margin 4px 6px
padding 3px 8px
border-radius 8px
box-shadow 0 1px 6px 0 var(--box-shadow-color)
color var(--white)
font-size 12px
text-align center
overflow hidden
cursor pointer
transition background-color 0.3s, transform 0.3s
&:hover
cursor pointer
&.active
transform scale(1.1, 1.1)
@require '~@mr-hope/vuepress-shared/styles/colors.styl'
for $color, $index in $colors
.tag-list-wrapper .tag{$index}
.theme-light &, &
background lighten($color, 10%)
&:hover, &.active
background darken($color, 5%)
.theme-dark &
background darken($color, 5%)
&:hover, &.active
background lighten($color, 10%)
</style>
import Anchor from "@theme/components/Anchor.vue";
import type { SidebarHeader } from "@theme/utils/groupHeader";
declare const _default: import("vue/types/vue").ExtendedVue<{
$timelineItems: import("@mr-hope/vuepress-types").PageComputed[];
$timeline: import("@theme/mixins/timeline").TimelineItem[];
} & Record<never, any> & Anchor, unknown, {
navigate(url: string): void;
}, {
hint: string;
anchorConfig: SidebarHeader[];
}, Record<never, any>>;
export default _default;
import Anchor from "@theme/components/Anchor.vue";
import MyTransition from "@theme/components/MyTransition.vue";
import { timelineMixin } from "@theme/mixins/timeline";
import { getDefaultLocale } from "@mr-hope/vuepress-shared";
export default timelineMixin.extend({
name: "Timeline",
components: { Anchor, MyTransition },
computed: {
hint() {
return ((this.$themeConfig.blog && this.$themeConfig.blog.timeline) ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.$themeLocaleConfig.blog.timelineText ||
getDefaultLocale().blog.timelineText);
},
anchorConfig() {
return this.$timeline.map((item) => ({
title: item.year.toString(),
level: 2,
slug: item.year.toString(),
}));
},
},
methods: {
navigate(url) {
void this.$router.push(url);
},
},
});
//# sourceMappingURL=Timeline.js.map
\ No newline at end of file
{"version":3,"file":"Timeline.js","sourceRoot":"","sources":["Timeline.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,8BAA8B,CAAC;AAClD,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAI5D,eAAe,aAAa,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,UAAU;IAEhB,UAAU,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE;IAEpC,QAAQ,EAAE;QACR,IAAI;YACF,OAAO,CACL,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;gBAC3D,oEAAoE;gBACpE,IAAI,CAAC,kBAAkB,CAAC,IAAK,CAAC,YAAY;gBAC1C,gBAAgB,EAAE,CAAC,IAAI,CAAC,YAAY,CACrC,CAAC;QACJ,CAAC;QAED,YAAY;YACV,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACnC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;gBAC3B,KAAK,EAAE,CAAC;gBACR,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;aAC3B,CAAC,CAAC,CAAC;QACN,CAAC;KACF;IAED,OAAO,EAAE;QACP,QAAQ,CAAC,GAAW;YAClB,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="timeline-wrapper">
<ul class="timeline-content">
<MyTransition>
<li class="desc">{{ hint }}</li>
</MyTransition>
<Anchor :items="anchorConfig" />
<MyTransition
v-for="(item, index) in $timeline"
:key="index"
:delay="0.08 * (index + 1)"
>
<li>
<h3 :id="item.year" class="year">
<span>{{ item.year }}</span>
</h3>
<ul class="year-wrapper">
<li
v-for="(article, articleIndex) in item.articles"
:key="articleIndex"
>
<span class="date">{{ article.frontmatter.parsedDate }}</span>
<span class="title" @click="navigate(article.path)">
{{ article.title }}
</span>
</li>
</ul>
</li>
</MyTransition>
</ul>
</div>
</template>
<script src="./Timeline" />
<style lang="stylus">
.timeline-wrapper
max-width 740px
margin 0 auto
padding 40px 0
--dot-color #fff
--dot-bar-color #eaecef
--dot-border-color #ddd
.theme-dark &
--dot-color #444
--dot-bar-color #333
--dot-border-color #555
#anchor
left unset
right 0
min-width 0
.anchor-wrapper
position relative
z-index 10
.timeline-content
box-sizing border-box
position relative
padding-left 76px
list-style none
&::after
content ' '
position absolute
top 14px
left 64px
z-index -1
width 4px
height calc(100% - 38px)
margin-left -2px
background var(--dot-bar-color)
.desc
position relative
color var(--text-color)
font-size 18px
@media (min-width $MQNormal)
font-size 20px
&:before
content ' '
position absolute
z-index 2
left -12px
top 50%
width 8px
height 8px
margin-left -6px
margin-top -6px
background var(--dot-color)
border 2px solid var(--dot-border-color)
border-radius 50%
.year
margin-top 0.5rem - $navbarHeight
margin-bottom 0.5rem
padding-top: ($navbarHeight + 3rem)
color var(--text-color)
font-size 26px
font-weight 700
span
position relative
&:before
content ' '
position absolute
z-index 2
left -12px
top 50%
width 8px
height 8px
margin-left -6px
margin-top -6px
background var(--dot-color)
border 2px solid var(--dot-border-color)
border-radius 50%
.year-wrapper
padding-left 0 !important
li
position relative
display flex
padding 30px 0 10px
border-bottom 1px dashed var(--border-color)
list-style none
&:hover
cursor pointer
.date
font-size 16px
transition font-size 0.3s ease-out
&::before
background-color var(--bgcolor)
border-color var(--accent-color)
.title
color var(--accent-color)
font-size 18px
transition font-size 0.3s ease-out
.date
position absolute
right calc(100% + 24px)
text-align right
width 40px
font-size 14px
line-height 30px
&::before
content ' '
position absolute
z-index 2
right -16px
top 50%
width 6px
height 6px
margin-left -6px
margin-top -6px
background var(--dot-color)
border 2px solid var(--dot-border-color)
border-radius 50%
.title
position relative
font-size 16px
line-height 30px
@media (max-width $MQMobile)
.timeline-wrapper
margin 0 1.2rem
</style>
D
import MyTransition from "@theme/components/MyTransition.vue";
declare const _default: import("vue/types/vue").ExtendedVue<{
$timelineItems: import("@mr-hope/vuepress-types").PageComputed[];
$timeline: import("@theme/mixins/timeline").TimelineItem[];
} & Record<never, any> & MyTransition, unknown, {
navigate(url: string): void;
}, {
hint: string;
}, Record<never, any>>;
export default _default;
import MyTransition from "@theme/components/MyTransition.vue";
import TimeIcon from "@mr-hope/vuepress-plugin-comment/lib/client/icons/TimeIcon.vue";
import { timelineMixin } from "@theme/mixins/timeline";
import { getDefaultLocale } from "@mr-hope/vuepress-shared";
export default timelineMixin.extend({
name: "TimelineList",
components: { MyTransition, TimeIcon },
computed: {
hint() {
return (
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
this.$themeLocaleConfig.blog.timeline ||
getDefaultLocale().blog.timeline);
},
},
methods: {
navigate(url) {
void this.$router.push(url);
},
},
});
//# sourceMappingURL=TimelineList.js.map
\ No newline at end of file
{"version":3,"file":"TimelineList.js","sourceRoot":"","sources":["TimelineList.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAC9D,OAAO,QAAQ,MAAM,gEAAgE,CAAC;AACtF,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,eAAe,aAAa,CAAC,MAAM,CAAC;IAClC,IAAI,EAAE,cAAc;IAEpB,UAAU,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE;IAEtC,QAAQ,EAAE;QACR,IAAI;YACF,OAAO;YACL,oEAAoE;YACpE,IAAI,CAAC,kBAAkB,CAAC,IAAK,CAAC,QAAQ;gBACtC,gBAAgB,EAAE,CAAC,IAAI,CAAC,QAAQ,CACjC,CAAC;QACJ,CAAC;KACF;IAED,OAAO,EAAE;QACP,QAAQ,CAAC,GAAW;YAClB,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="timeline-list-wrapper">
<div class="title" @click="navigate('/timeline/')">
<TimeIcon />
<span class="num">{{ $timelineItems.length }}</span>
{{ hint }}
</div>
<hr />
<div class="content">
<ul class="timeline-list">
<MyTransition
v-for="(item, index) in $timeline"
:key="index"
:delay="0.08 * (index + 1)"
>
<li>
<h3 class="year">{{ item.year }}</h3>
<ul class="year-wrapper">
<li
v-for="(article, articleIndex) in item.articles"
:key="articleIndex"
>
<span class="date">{{ article.frontmatter.parsedDate }}</span>
<span class="timeline-title" @click="navigate(article.path)">
{{ article.title }}
</span>
</li>
</ul>
</li>
</MyTransition>
</ul>
</div>
</div>
</template>
<script src="./TimelineList" />
<style lang="stylus">
.timeline-list-wrapper
padding 8px 0
--dot-color #fff
--dot-bar-color #eaecef
--dot-border-color #ddd
.theme-dark &
--dot-color #444
--dot-bar-color #333
--dot-border-color #555
.title
cursor pointer
.icon
position relative
bottom -0.125rem
width 16px
height 16px
margin 0 6px
.num
position relative
margin 0 2px
font-size 22px
.content
overflow-y scroll
max-height 80vh
&::-webkit-scrollbar-track-piece
background transparent
.timeline-list
position relative
margin 0 8px
box-sizing border-box
list-style none
&::after
content ' '
position absolute
top 14px
left 0
z-index -1
margin-left -2px
width 4px
height calc(100% - 14px)
background var(--dot-bar-color)
.year
position relative
margin 20px 0 0px
color var(--text-color)
font-size 20px
font-weight 700
&:before
content ' '
position absolute
z-index 2
left -20px
top 50%
margin-left -4px
margin-top -4px
width 8px
height 8px
background var(--dot-color)
border 1px solid var(--dot-border-color)
border-radius 50%
.year-wrapper
padding-left 0 !important
li
position relative
display flex
padding 12px 0 4px
list-style none
border-bottom 1px dashed var(--border-color)
&:hover
.date
color var(--accent-color)
&::before
background var(--accent-color)
border-color var(--dot-color)
.title
color var(--accent-color)
.date
width 36px
line-height 32px
display inline-block
vertical-align bottom
font-size 12px
&::before
content ' '
position absolute
left -19px
top 24px
width 6px
height 6px
margin-left -4px
background var(--dot-color)
border-radius 50%
border 1px solid var(--dot-border-color)
z-index 2
.timeline-title
line-height 32px
font-size 14px
cursor pointer
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
location: string;
}, unknown, {
copyright: string;
}, {
html: string;
lang: string;
}>;
export default _default;
import Vue from "vue";
export default Vue.extend({
name: "Clipboard",
props: {
html: { type: String, default: "" },
lang: { type: String, default: "en-US" },
},
data: () => ({
location: "",
}),
computed: {
copyright() {
const { author } = this.$themeConfig;
const content = {
"zh-CN": `${this.html}\n-----\n${author ? `著作权归${author}所有。\n` : ""}链接: ${this.location}`,
"en-US": `${this.html}\n-----\n${author ? `Copyright by ${author}.\n` : ""}Link: ${this.location}`,
"vi-VN": `${this.html}\n-----\n${author ? `bản quyền bởi ${author}.\n` : ""}Liên kết: ${this.location}`,
};
return content[this.lang];
},
},
created() {
if (typeof window !== "undefined")
this.location = window.location.toString();
},
});
//# sourceMappingURL=Clipboard.js.map
\ No newline at end of file
{"version":3,"file":"Clipboard.js","sourceRoot":"","sources":["Clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,WAAW;IAEjB,KAAK,EAAE;QACL,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACnC,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE;KACzC;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,QAAQ,EAAE,EAAE;KACb,CAAC;IAEF,QAAQ,EAAE;QACR,SAAS;YACP,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;YACrC,MAAM,OAAO,GAA2B;gBACtC,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,YACnB,MAAM,CAAC,CAAC,CAAC,OAAO,MAAM,OAAO,CAAC,CAAC,CAAC,EAClC,OAAO,IAAI,CAAC,QAAQ,EAAE;gBACtB,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,YACnB,MAAM,CAAC,CAAC,CAAC,gBAAgB,MAAM,KAAK,CAAC,CAAC,CAAC,EACzC,SAAS,IAAI,CAAC,QAAQ,EAAE;gBACxB,OAAO,EAAE,GAAG,IAAI,CAAC,IAAI,YACnB,MAAM,CAAC,CAAC,CAAC,iBAAiB,MAAM,KAAK,CAAC,CAAC,CAAC,EAC1C,aAAa,IAAI,CAAC,QAAQ,EAAE;aAC7B,CAAC;YAEF,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;KACF;IAED,OAAO;QACL,IAAI,OAAO,MAAM,KAAK,WAAW;YAC/B,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAC/C,CAAC;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-html="copyright" />
</template>
<script src="./Clipboard" />
import Navbar from "@theme/components/Navbar/Navbar.vue";
import type { SidebarItem, SidebarHeader } from "@theme/utils/sidebar";
declare const _default: import("vue/types/vue").ExtendedVue<{
globalEncryptPassword: string;
} & {
checkGlobalPassword(globalPassword: string): void;
} & {
isGlobalEncrypted: boolean;
} & Record<never, any> & {
encryptOptions: import("../types").EncryptOptions;
} & Navbar, {
isSidebarOpen: boolean;
hideNavbar: boolean;
touchStart: {
clientX: number;
clientY: number;
};
}, {
/** Get scroll distance */
getScrollTop(): number;
toggleSidebar(to: boolean): void;
onTouchStart(event: TouchEvent): void;
onTouchEnd(event: TouchEvent): void;
getHeader(items: SidebarItem[]): SidebarHeader[];
}, {
enableNavbar: boolean;
enableSidebar: boolean;
sidebarItems: SidebarItem[];
pageClasses: unknown;
headers: SidebarHeader[];
enableAnchor: boolean;
}, {
navbar: boolean;
sidebar: boolean;
}>;
export default _default;
import { getSidebarItems } from "@theme/utils/sidebar";
import { globalEncryptMixin } from "@theme/mixins/globalEncrypt";
import Navbar from "@theme/components/Navbar/Navbar.vue";
import PageFooter from "@theme/components/PageFooter.vue";
import Password from "@theme/components/Password.vue";
import Sidebar from "@theme/components/Sidebar/Sidebar.vue";
import throttle from "lodash.throttle";
export default globalEncryptMixin.extend({
name: "Common",
components: {
Navbar,
PageFooter,
Password,
Sidebar,
},
props: {
navbar: { type: Boolean, default: true },
sidebar: { type: Boolean, default: true },
},
data: () => ({
isSidebarOpen: false,
hideNavbar: false,
touchStart: {
clientX: 0,
clientY: 0,
},
}),
computed: {
enableNavbar() {
if (this.navbar === false)
return false;
const { frontmatter } = this.$page;
if (frontmatter.navbar === false || this.$themeConfig.navbar === false)
return false;
return Boolean(this.$title ||
this.$themeConfig.logo ||
this.$themeConfig.repo ||
this.$themeConfig.nav ||
this.$themeLocaleConfig.nav);
},
enableSidebar() {
if (this.sidebar === false)
return false;
return (!this.$frontmatter.home &&
this.$frontmatter.sidebar !== false &&
this.sidebarItems.length !== 0);
},
sidebarItems() {
if (this.sidebar === false)
return [];
return getSidebarItems(this.$page, this.$site, this.$localePath);
},
pageClasses() {
const userPageClass = this.$page.frontmatter.pageClass;
return [
{
"has-navbar": this.enableNavbar,
"has-sidebar": this.enableSidebar,
"has-anchor": this.enableAnchor,
"hide-navbar": this.hideNavbar,
"sidebar-open": this.isSidebarOpen,
},
userPageClass,
];
},
headers() {
return this.getHeader(this.sidebarItems);
},
enableAnchor() {
return (this.$frontmatter.anchorDisplay ||
(this.$themeConfig.anchorDisplay !== false &&
this.$frontmatter.anchorDisplay !== false));
},
},
mounted() {
let lastDistance = 0;
this.$router.afterEach(() => {
this.isSidebarOpen = false;
});
window.addEventListener("scroll", throttle(() => {
const distance = this.getScrollTop();
// scroll down
if (lastDistance < distance && distance > 58) {
if (!this.isSidebarOpen)
this.hideNavbar = true;
// scroll up
}
else
this.hideNavbar = false;
lastDistance = distance;
}, 300));
},
methods: {
/** Get scroll distance */
getScrollTop() {
return (window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop ||
0);
},
toggleSidebar(to) {
this.isSidebarOpen = typeof to === "boolean" ? to : !this.isSidebarOpen;
this.$emit("toggle-sidebar", this.isSidebarOpen);
},
// Side swipe
onTouchStart(event) {
this.touchStart = {
clientX: event.changedTouches[0].clientX,
clientY: event.changedTouches[0].clientY,
};
},
onTouchEnd(event) {
const dx = event.changedTouches[0].clientX - this.touchStart.clientX;
const dy = event.changedTouches[0].clientY - this.touchStart.clientY;
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40)
if (dx > 0 && this.touchStart.clientX <= 80)
this.toggleSidebar(true);
else
this.toggleSidebar(false);
},
getHeader(items) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.type === "group") {
const matching = this.getHeader(item.children);
if (matching.length !== 0)
return matching;
}
else if (item.type === "page" &&
item.headers &&
item.path === this.$route.path)
return item.headers;
}
return [];
},
},
});
//# sourceMappingURL=Common.js.map
\ No newline at end of file
{"version":3,"file":"Common.js","sourceRoot":"","sources":["Common.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<template>
<div
class="theme-container"
:class="pageClasses"
@touchstart="onTouchStart"
@touchend="onTouchEnd"
>
<Password v-if="isGlobalEncrypted" @password-verify="checkGlobalPassword" />
<!-- Content -->
<template v-else>
<Navbar v-if="enableNavbar" @toggle-sidebar="toggleSidebar">
<template #start>
<slot name="navbar-start" />
</template>
<template #center>
<slot name="navbar-center" />
</template>
<template #end>
<slot name="navbar-end" />
</template>
</Navbar>
<div class="sidebar-mask" @click="toggleSidebar(false)" />
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar">
<template #top>
<slot name="sidebar-top" />
</template>
<template #center>
<slot name="sidebar-center" />
</template>
<template #bottom>
<slot name="sidebar-bottom" />
</template>
</Sidebar>
<slot :sidebar-items="sidebarItems" :headers="headers" />
<PageFooter :key="$route.path" />
</template>
</div>
</template>
<script src="./Common" />
<style lang="stylus">
.theme-container
min-height 100vh
.sidebar-mask
position fixed
z-index 9
top 0
left 0
width 100vw
height 100vh
display none
.theme-container.sidebar-open &
display block
</style>
import Vue from "vue";
interface ActionConfig {
text: string;
link: string;
}
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
navigate(link: string): void;
}, {
actionLinks: ActionConfig[];
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import MyTransition from "@theme/components/MyTransition.vue";
import NavLink from "@theme/components/Navbar/NavLink.vue";
import { navigate } from "@theme/utils/navigate";
export default Vue.extend({
name: "Home",
components: { MyTransition, NavLink },
computed: {
actionLinks() {
const { action } = this.$frontmatter;
if (Array.isArray(action))
return action;
return [action];
},
},
methods: {
navigate(link) {
navigate(link, this.$router, this.$route);
},
},
});
//# sourceMappingURL=Home.js.map
\ No newline at end of file
{"version":3,"file":"Home.js","sourceRoot":"","sources":["Home.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAC9D,OAAO,OAAO,MAAM,sCAAsC,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAOjD,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM;IAEZ,UAAU,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE;IAErC,QAAQ,EAAE;QACR,WAAW;YACT,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;YACrC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,OAAO,MAAwB,CAAC;YAC3D,OAAO,CAAC,MAAM,CAAmB,CAAC;QACpC,CAAC;KACF;IAED,OAAO,EAAE;QACP,QAAQ,CAAC,IAAY;YACnB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC5C,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<main
:aria-labelledby="$frontmatter.heroText !== null ? 'main-title' : null"
class="home"
>
<header class="hero">
<MyTransition>
<img
v-if="$frontmatter.heroImage"
key="light"
:class="{ light: Boolean($frontmatter.darkHeroImage) }"
:src="$withBase($frontmatter.heroImage)"
:alt="$frontmatter.heroAlt || 'HomeLogo'"
/>
</MyTransition>
<MyTransition>
<img
v-if="$frontmatter.darkHeroImage"
key="dark"
class="dark"
:src="$withBase($frontmatter.darkHeroImage)"
:alt="$frontmatter.heroAlt || 'HomeLogo'"
/>
</MyTransition>
<div class="hero-info">
<MyTransition :delay="0.04">
<h1
v-if="$frontmatter.heroText !== false"
id="main-title"
v-text="$frontmatter.heroText || $title || 'Hello'"
/>
</MyTransition>
<MyTransition :delay="0.08">
<p
class="description"
v-text="
$frontmatter.tagline ||
$description ||
'Welcome to your VuePress site'
"
/>
</MyTransition>
<MyTransition :delay="0.12">
<p v-if="$frontmatter.action" class="action">
<NavLink
v-for="action in actionLinks"
:key="action.text"
:item="action"
class="action-button"
:class="action.type || ''"
/>
</p>
</MyTransition>
</div>
</header>
<MyTransition :delay="0.16">
<div>
<h2 class="features-header">Resources</h2>
<div
v-if="$frontmatter.features && $frontmatter.features.length"
class="features"
>
<template v-for="(feature, index) in $frontmatter.features">
<div
v-if="feature.link"
:key="index"
class="feature link"
:class="`feature${index % 9}`"
tabindex="0"
role="navigation"
@click="navigate(feature.link)"
>
<div class="icon-container">
<i :class="`far fa-${feature.icon}`"></i>
</div>
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</div>
<div
v-else
:key="index"
class="feature"
:class="`feature${index % 9}`"
>
<h2>{{ feature.title }}</h2>
<p>{{ feature.details }}</p>
</div>
</template>
</div>
</div>
</MyTransition>
<MyTransition :delay="0.24">
<Content class="theme-default-content custom" />
</MyTransition>
</main>
</template>
<script src="./Home" />
<style lang="stylus">
.home
display block
max-width $homePageWidth
min-height 100vh - $navbarHeight
padding $navbarHeight 2rem 0
margin 0px auto
overflow-x hidden
@media (max-width $MQNarrow)
min-height 100vh - $navbarMobileHeight
padding-top $navbarMobileHeight
@media (max-width $MQMobileNarrow)
padding-left 1.5rem
padding-right 1.5rem
.hero
text-align center
@media (min-width $MQNarrow)
display flex
justify-content space-evenly
align-items center
text-align left
img
display block
max-width 100%
max-height 320px
margin 0
@media (max-width $MQNarrow)
max-height 280px
margin 3rem auto 1.5rem
@media (max-width $MQMobile)
max-height 240px
margin 2rem auto 1.2rem
@media (max-width $MQMobileNarrow)
max-height 210px
margin 1.5rem auto 1rem
.theme-light &
&.light
display block
&.dark
display none
.theme-dark &
&.light
display none
&.dark
display block
h1
font-size 3rem
@media (max-width $MQMobile)
font-size 2.5rem
@media (max-width $MQMobileNarrow)
font-size 2rem
h1, .description, .action
margin 1.8rem auto
@media (max-width $MQMobile)
margin 1.5rem auto
@media (max-width $MQMobileNarrow)
margin 1.2rem auto
.description
max-width 35rem
color var(--text-color-l40)
font-size 1.6rem
line-height 1.3
@media (max-width $MQMobile)
font-size 1.4rem
@media (max-width $MQMobileNarrow)
font-size 1.2rem
.action-button
display inline-block
margin 0.6rem 0.8rem
padding 1rem 1.5rem
border 2px solid var(--accent-color)
border-radius 2rem
color var(--accent-color)
font-size 1.2rem
transition background 0.1s ease
overflow hidden
@media (max-width $MQMobile)
padding 0.8rem 1.2rem
font-size 1.1rem
@media (max-width $MQMobileNarrow)
font-size 1rem
&:hover
color var(--white)
background-color var(--accent-color)
&.primary
color var(--white)
background-color var(--accent-color)
&:hover
border-color var(--accent-color-l10)
background-color var(--accent-color-l10)
.theme-dark &
&:hover
border-color var(--accent-color-d10)
background-color var(--accent-color-d10)
.features
display flex
flex-wrap wrap
justify-content center
align-items stretch
align-content stretch
margin 0 -2rem
padding 1.2rem 0
border-top 1px solid var(--border-color)
@media (max-width $MQMobileNarrow)
margin 0 -1.5rem
.feature
display flex
flex-direction column
justify-content center
flex-basis calc(33% - 4rem)
margin 0.5rem
padding 0 1.5rem
border-radius 0.5rem
transition transform 0.3s, box-shadow 0.3s
overflow hidden
@media (max-width $MQNarrow)
flex-basis calc(50% - 4rem)
@media (max-width $MQMobile)
font-size 0.95rem
@media (max-width $MQMobileNarrow)
flex-basis calc(100%)
font-size 0.9rem
margin 0.5rem 0
border-radius 0
&.link
cursor pointer
&:hover
box-shadow 0 2px 12px 0 var(--card-shadow-color)
h2
margin-bottom 0.25rem
border-bottom none
color var(--text-color-l10)
font-size 1.25rem
font-weight 500
@media (max-width $MQMobileNarrow)
font-size 1.2rem
p
margin-top 0
color var(--text-color-l25)
{$contentClass}
padding-bottom 1.5rem
@require '~@mr-hope/vuepress-shared/styles/colors.styl'
for $color, $index in $colors
.home .features .feature{$index}
&, .theme-light &
background lighten($color, 90%)
.theme-dark &
background darken($color, 75%)
</style>
import Vue from "vue";
import type { BlogMedia } from "@theme/types";
interface MediaLink {
icon: string;
url: string;
}
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
mediaLink: false | Partial<Record<BlogMedia, string>>;
links: MediaLink[];
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import Baidu from "@theme/icons/media/Baidu.vue";
import Bitbucket from "@theme/icons/media/Bitbucket.vue";
import Dingding from "@theme/icons/media/Dingding.vue";
import Discord from "@theme/icons/media/Discord.vue";
import Dribbble from "@theme/icons/media/Dribbble.vue";
import Email from "@theme/icons/media/Email.vue";
import Evernote from "@theme/icons/media/Evernote.vue";
import Facebook from "@theme/icons/media/Facebook.vue";
import Flipboard from "@theme/icons/media/Flipboard.vue";
import Gitee from "@theme/icons/media/Gitee.vue";
import Github from "@theme/icons/media/Github.vue";
import Gitlab from "@theme/icons/media/Gitlab.vue";
import Gmail from "@theme/icons/media/Gmail.vue";
import Instagram from "@theme/icons/media/Instagram.vue";
import Lines from "@theme/icons/media/Lines.vue";
import Linkedin from "@theme/icons/media/Linkedin.vue";
import Pinterest from "@theme/icons/media/Pinterest.vue";
import Pocket from "@theme/icons/media/Pocket.vue";
import QQ from "@theme/icons/media/QQ.vue";
import Qzone from "@theme/icons/media/Qzone.vue";
import Reddit from "@theme/icons/media/Reddit.vue";
import Rss from "@theme/icons/media/Rss.vue";
import Steam from "@theme/icons/media/Steam.vue";
import Twitter from "@theme/icons/media/Twitter.vue";
import Wechat from "@theme/icons/media/Wechat.vue";
import Weibo from "@theme/icons/media/Weibo.vue";
import Whatsapp from "@theme/icons/media/Whatsapp.vue";
import Youtube from "@theme/icons/media/Youtube.vue";
import Zhihu from "@theme/icons/media/Zhihu.vue";
const medias = [
"Baidu",
"Bitbucket",
"Dingding",
"Discord",
"Dribbble",
"Email",
"Evernote",
"Facebook",
"Flipboard",
"Gitee",
"Github",
"Gitlab",
"Gmail",
"Instagram",
"Lines",
"Linkedin",
"Pinterest",
"Pocket",
"QQ",
"Qzone",
"Reddit",
"Rss",
"Steam",
"Twitter",
"Wechat",
"Weibo",
"Whatsapp",
"Youtube",
"Zhihu",
];
export default Vue.extend({
name: "MediaLinks",
components: {
Baidu,
Bitbucket,
Dingding,
Discord,
Dribbble,
Email,
Evernote,
Facebook,
Flipboard,
Gitee,
Github,
Gitlab,
Gmail,
Instagram,
Lines,
Linkedin,
Pinterest,
Pocket,
QQ,
Qzone,
Reddit,
Rss,
Steam,
Twitter,
Wechat,
Weibo,
Whatsapp,
Youtube,
Zhihu,
},
computed: {
mediaLink() {
const { medialink } = this.$frontmatter;
return medialink === false
? false
: typeof medialink === "object"
? medialink
: this.$themeConfig.blog
? this.$themeConfig.blog.links || false
: false;
},
links() {
if (this.mediaLink) {
const links = [];
for (const media in this.mediaLink)
if (medias.includes(media))
links.push({
icon: media,
url: this.mediaLink[media],
});
return links;
}
return [];
},
},
});
//# sourceMappingURL=MediaLinks.js.map
\ No newline at end of file
{"version":3,"file":"MediaLinks.js","sourceRoot":"","sources":["MediaLinks.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<template>
<div v-if="mediaLink" class="media-links-wrapper">
<a
v-for="link in links"
:key="link.name"
class="media-link"
:href="link.url"
rel="noopener noreferrer"
target="_blank"
:aria-label="link.icon"
data-balloon-pos="up"
>
<span class="sr-only" v-text="link.icon" />
<Component :is="link.icon" />
</a>
</div>
</template>
<script src="./MediaLinks" />
<style lang="stylus">
.media-links-wrapper
display flex
justify-content center
flex-wrap wrap
margin 8px auto
.media-link
width 26px
height 26px
margin 4px
transform scale(1, 1)
transition transform 0.18s ease-out 0.18s
&:hover
cursor pointer
transform scale(1.2, 1.2)
&::after
--balloon-font-size 8px
padding 0.3em 0.6em
.icon
width 100%
height 100%
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
setStyle(items: HTMLElement): void;
unsetStyle(items: HTMLElement): void;
}, unknown, {
delay: number;
duration: number;
}>;
export default _default;
import Vue from "vue";
export default Vue.extend({
name: "MyTransition",
props: {
delay: { type: Number, default: 0 },
duration: { type: Number, default: 0.25 },
disable: { type: Boolean, default: false },
},
methods: {
setStyle(items) {
if (this.disable) {
return;
}
items.style.transition = `transform ${this.duration}s ease-in-out ${this.delay}s, opacity ${this.duration}s ease-in-out ${this.delay}s`;
items.style.transform = "translateY(-20px)";
items.style.opacity = "0";
},
unsetStyle(items) {
if (this.disable) {
return;
}
items.style.transform = "translateY(0)";
items.style.opacity = "1";
},
},
});
//# sourceMappingURL=MyTransition.js.map
\ No newline at end of file
{"version":3,"file":"MyTransition.js","sourceRoot":"","sources":["MyTransition.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,cAAc;IAEpB,KAAK,EAAE;QACL,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE;QACnC,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE;KAC1C;IAED,OAAO,EAAE;QACP,QAAQ,CAAC,KAAkB;YACzB,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,aAAa,IAAI,CAAC,QAAQ,iBAAiB,IAAI,CAAC,KAAK,cAAc,IAAI,CAAC,QAAQ,iBAAiB,IAAI,CAAC,KAAK,GAAG,CAAC;YACxI,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,mBAAmB,CAAC;YAC5C,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;QAC5B,CAAC;QAED,UAAU,CAAC,KAAkB;YAC3B,KAAK,CAAC,KAAK,CAAC,SAAS,GAAG,eAAe,CAAC;YACxC,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;QAC5B,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<transition
name="drop"
appear
@appear="setStyle"
@after-appear="unsetStyle"
@enter="setStyle"
@after-enter="unsetStyle"
@before-leave="setStyle"
>
<slot />
</transition>
</template>
<script src="./MyTransition" />
<style lang="stylus">
.drop-enter, .drop-leave-to
opacity 0
transform translateY(-20px)
</style>
import Vue from "vue";
import type { NavBarConfigItem } from "@theme/utils/navbar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
open: boolean;
}, {
setOpen(value: boolean): void;
handleDropdown(event: MouseEvent): void;
isLastItemOfArray(item: NavBarConfigItem, array: NavBarConfigItem[]): boolean;
}, {
dropdownAriaLabel: string;
iconPrefix: string;
}, {
item: NavBarConfigItem;
}>;
export default _default;
import Vue from "vue";
import NavLink from "@theme/components/Navbar/NavLink.vue";
export default Vue.extend({
name: "DropdownLink",
components: { NavLink },
props: {
item: { type: Object, required: true },
},
data: () => ({
open: false,
}),
computed: {
dropdownAriaLabel() {
return this.item.ariaLabel || this.item.text;
},
iconPrefix() {
const { iconPrefix } = this.$themeConfig;
return iconPrefix === "" ? "" : iconPrefix || "icon-";
},
},
watch: {
$route() {
this.open = false;
},
},
methods: {
setOpen(value) {
this.open = value;
},
handleDropdown(event) {
const isTriggerByTab = event.detail === 0;
if (isTriggerByTab)
this.setOpen(!this.open);
},
isLastItemOfArray(item, array) {
if (Array.isArray(array))
return item === array[array.length - 1];
return false;
},
},
});
//# sourceMappingURL=DropdownLink.js.map
\ No newline at end of file
{"version":3,"file":"DropdownLink.js","sourceRoot":"","sources":["DropdownLink.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,OAAO,MAAM,sCAAsC,CAAC;AAK3D,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,cAAc;IAEpB,UAAU,EAAE,EAAE,OAAO,EAAE;IAEvB,KAAK,EAAE;QACL,IAAI,EAAE,EAAE,IAAI,EAAE,MAAoC,EAAE,QAAQ,EAAE,IAAI,EAAE;KACrE;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,IAAI,EAAE,KAAK;KACZ,CAAC;IAEF,QAAQ,EAAE;QACR,iBAAiB;YACf,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC/C,CAAC;QAED,UAAU;YACR,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;YAEzC,OAAO,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC;QACxD,CAAC;KACF;IAED,KAAK,EAAE;QACL,MAAM;YACJ,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QACpB,CAAC;KACF;IAED,OAAO,EAAE;QACP,OAAO,CAAC,KAAc;YACpB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QACpB,CAAC;QAED,cAAc,CAAC,KAAiB;YAC9B,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC;YAC1C,IAAI,cAAc;gBAAE,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;QAED,iBAAiB,CACf,IAAsB,EACtB,KAAyB;YAEzB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAElE,OAAO,KAAK,CAAC;QACf,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="dropdown-wrapper" :class="{ open }">
<button
class="dropdown-title"
type="button"
:aria-label="dropdownAriaLabel"
@click="handleDropdown"
>
<slot name="title">
<span class="title">
<i v-if="item.icon" :class="`iconfont ${iconPrefix}${item.icon}`" />
{{ item.text }}
</span>
</slot>
<span class="arrow" />
</button>
<ul class="nav-dropdown">
<li
v-for="(child, index) in item.items"
:key="child.link || index"
class="dropdown-item"
>
<template v-if="child.type === 'links'">
<h4 class="dropdown-subtitle">
<NavLink
v-if="child.link"
:item="child"
@focusout="
isLastItemOfArray(child, item.children) &&
child.children.length === 0 &&
setOpen(false)
"
/>
<span v-else>{{ child.text }}</span>
</h4>
<ul class="dropdown-subitem-wrapper">
<li
v-for="grandchild in child.items"
:key="grandchild.link"
class="dropdown-subitem"
>
<NavLink
:item="grandchild"
@focusout="
isLastItemOfArray(grandchild, child.items) &&
isLastItemOfArray(child, item.items) &&
setOpen(false)
"
/>
</li>
</ul>
</template>
<NavLink
v-else
:item="child"
@focusout="isLastItemOfArray(child, item.items) && setOpen(false)"
/>
</li>
</ul>
</div>
</template>
<script src="./DropdownLink" />
<style lang="stylus">
@require '~@mr-hope/vuepress-shared/styles/arrow'
@require '~@mr-hope/vuepress-shared/styles/reset'
.dropdown-wrapper
height 1.8rem
cursor pointer
&:not(:hover)
.arrow
transform rotate(-180deg)
&:hover, &.open
.nav-dropdown
z-index 2
transform scale(1)
visibility visible
opacity 1
.dropdown-title
button()
cursor inherit
padding inherit
color var(--dark-grey)
font-family inherit
font-size 0.9rem
font-weight 500
line-height 1.4rem
&::after
border-left 5px solid var(--accent-color)
&:hover
border-color transparent
.arrow
arrow()
font-size 1.2em
.nav-dropdown
box-sizing border-box
position absolute
top 100%
right 0
max-height 100vh - $navbarHeight
margin 0
padding 0.6rem 0
border 1px solid var(--grey14)
border-radius 0.25rem
background var(--bgcolor)
box-shadow 2px 2px 10px var(--card-shadow-color)
text-align left
white-space nowrap
overflow-y auto
transform scale(0.8)
opacity 0
visibility hidden
transition all 0.18s ease-out
.dropdown-item
color inherit
line-height 1.7rem
h4
margin 0
padding 0.75rem 1rem 0.25rem 0.75rem
border-top 1px solid var(--grey14)
color var(--dark-grey)
font-size 0.9rem
.nav-link
padding 0
&:before
display none
&:first-child h4
padding-top 0
border-top 0
.nav-link
display block
position relative
margin-bottom 0
padding 0 1.5rem 0 1.25rem
border-bottom none
color var(--dark-grey)
font-weight 400
line-height 1.7rem
&:hover
color var(--accent-color)
&.active
color var(--accent-color)
&::before
content ''
position absolute
top calc(50% - 3px)
left 9px
width 0
height 0
border-top 3px solid transparent
border-left 5px solid var(--accent-color)
border-bottom 3px solid transparent
.dropdown-subitem-wrapper
padding 0
list-style none
.dropdown-subitem
font-size 0.9em
</style>
import Vue from "vue";
import type { NavBarConfigItem as ResovledNavbarConfigItem } from "@theme/utils/navbar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
dropdown: false | ResovledNavbarConfigItem;
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import DropdownLink from "@theme/components/Navbar/DropdownLink.vue";
import I18nIcon from "@theme/icons/I18nIcon.vue";
import NavLink from "@theme/components/Navbar/NavLink.vue";
import { getNavLinkItem } from "@theme/utils/navbar";
export default Vue.extend({
name: "LanguageDropdown",
components: { NavLink, DropdownLink },
computed: {
dropdown() {
const { locales } = this.$site;
if (locales && Object.keys(locales).length > 1) {
const currentLink = this.$page.path;
const { routes } = this.$router.options;
const themeLocales = this.$themeConfig.locales || {};
const languageDropdown = {
text: this.$themeLocaleConfig.selectText || "Languages",
ariaLabel: this.$themeLocaleConfig.ariaLabel || "Select language",
items: Object.keys(locales).map((path) => {
const locale = locales[path];
const text = (themeLocales[path] && themeLocales[path].label) ||
locale.lang ||
"Unknown Language";
let link;
// Stay on the current page
if (locale.lang === this.$lang)
link = currentLink;
else {
// Try to stay on the same page
link = currentLink.replace(this.$localeConfig.path, path);
// Fallback to homepage
if (!(routes || []).some((route) => route.path === link))
link = path;
}
return { text, link };
}),
};
return getNavLinkItem(languageDropdown);
}
return false;
},
},
render(h) {
return this.dropdown
? h("div", { class: "nav-links" }, [
h("div", { class: "nav-item" }, [
h(DropdownLink, { props: { item: this.dropdown } }, [
h(I18nIcon, {
slot: "title",
style: {
width: "1rem",
height: "1rem",
verticalAlign: "middle",
marginLeft: "1rem",
},
}),
]),
]),
])
: null;
},
});
//# sourceMappingURL=LanguageDropdown.js.map
\ No newline at end of file
{"version":3,"file":"LanguageDropdown.js","sourceRoot":"","sources":["LanguageDropdown.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
import Vue from "vue";
import type { NavBarConfigItem } from "@theme/utils/navbar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
focusoutAction(): void;
}, {
link: string;
iconPrefix: string;
active: boolean;
isNonHttpURI: boolean;
isBlankTarget: boolean;
isInternal: boolean;
target: string | null;
rel: string | null;
}, {
item: NavBarConfigItem;
}>;
export default _default;
import Vue from "vue";
import { ensureExt, isExternal, isMailto, isTel } from "@theme/utils/path";
export default Vue.extend({
name: "NavLink",
props: {
item: { type: Object, required: true },
},
computed: {
link() {
return ensureExt(this.item.link);
},
iconPrefix() {
const { iconPrefix } = this.$themeConfig;
return iconPrefix === "" ? "" : iconPrefix || "icon-";
},
active() {
// link is home path
if ((this.$site.locales &&
Object.keys(this.$site.locales).some((rootLink) => rootLink === this.link)) ||
this.link === "/")
// exact match
return this.$route.path === this.link;
// inclusive match
return this.$route.path.startsWith(this.link);
},
isNonHttpURI() {
return isMailto(this.link) || isTel(this.link);
},
isBlankTarget() {
return this.target === "_blank";
},
isInternal() {
return !isExternal(this.link) && !this.isBlankTarget;
},
target() {
if (this.isNonHttpURI)
return null;
if (this.item.target)
return this.item.target;
return isExternal(this.link) ? "_blank" : "";
},
rel() {
if (this.isNonHttpURI)
return null;
if (this.item.rel === false)
return null;
if (this.item.rel)
return this.item.rel;
return this.isBlankTarget ? "noopener noreferrer" : null;
},
},
methods: {
focusoutAction() {
// eslint-disable-next-line vue/require-explicit-emits
this.$emit("focusout");
},
},
});
//# sourceMappingURL=NavLink.js.map
\ No newline at end of file
{"version":3,"file":"NavLink.js","sourceRoot":"","sources":["NavLink.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAK3E,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,SAAS;IAEf,KAAK,EAAE;QACL,IAAI,EAAE,EAAE,IAAI,EAAE,MAAoC,EAAE,QAAQ,EAAE,IAAI,EAAE;KACrE;IAED,QAAQ,EAAE;QACR,IAAI;YACF,OAAO,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAc,CAAC,CAAC;QAC7C,CAAC;QAED,UAAU;YACR,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;YAEzC,OAAO,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC;QACxD,CAAC;QAED,MAAM;YACJ,oBAAoB;YACpB,IACE,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO;gBACjB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAClC,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,KAAK,IAAI,CAAC,IAAI,CACrC,CAAC;gBACJ,IAAI,CAAC,IAAI,KAAK,GAAG;gBAEjB,cAAc;gBACd,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC;YAExC,kBAAkB;YAClB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;QAED,YAAY;YACV,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjD,CAAC;QAED,aAAa;YACX,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC;QAClC,CAAC;QAED,UAAU;YACR,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC;QACvD,CAAC;QAED,MAAM;YACJ,IAAI,IAAI,CAAC,YAAY;gBAAE,OAAO,IAAI,CAAC;YAEnC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM;gBAAE,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;YAE9C,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/C,CAAC;QAED,GAAG;YACD,IAAI,IAAI,CAAC,YAAY;gBAAE,OAAO,IAAI,CAAC;YACnC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,KAAK,KAAK;gBAAE,OAAO,IAAI,CAAC;YACzC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG;gBAAE,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;YAExC,OAAO,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3D,CAAC;KACF;IAED,OAAO,EAAE;QACP,cAAc;YACZ,sDAAsD;YACtD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACzB,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<!-- eslint-disable vue/no-deprecated-v-on-native-modifier -->
<RouterLink
v-if="isInternal"
class="nav-link"
:class="{ active }"
:to="link"
@focusout.native="focusoutAction"
>
<i v-if="item.icon" :class="`iconfont ${item.iconPrefix || iconPrefix}${item.icon} ${item.iconClass}`" />
{{ item.text }}
</RouterLink>
<a
v-else
class="nav-link external"
:href="link"
:target="target"
:rel="rel"
@focusout="focusoutAction"
>
<i v-if="item.icon" :class="`iconfont ${item.iconPrefix || iconPrefix}${item.icon} ${item.iconClass}`" />
{{ item.text }}
<OutboundLink v-if="isBlankTarget" />
</a>
</template>
<script src="./NavLink" />
<style lang="stylus">
.nav-link
line-height 1.4rem
.navbar &
color var(--dark-grey)
&.active
color var(--accent-color)
.sidebar &
color var(--text-color)
&:hover, &.active
color var(--accent-color)
</style>
import Vue from "vue";
import type { NavBarConfigItem as ResovledNavbarConfigItem } from "@theme/utils/navbar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
navLinks: ResovledNavbarConfigItem[];
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import DropdownLink from "@theme/components/Navbar/DropdownLink.vue";
import NavLink from "@theme/components/Navbar/NavLink.vue";
import { getNavLinkItem } from "@theme/utils/navbar";
export default Vue.extend({
name: "NavLinks",
components: {
DropdownLink,
NavLink,
},
computed: {
navLinks() {
const navbar = this.$themeLocaleConfig.nav || this.$themeConfig.nav || [];
return navbar.map((link) => getNavLinkItem(link));
},
},
});
//# sourceMappingURL=NavLinks.js.map
\ No newline at end of file
{"version":3,"file":"NavLinks.js","sourceRoot":"","sources":["NavLinks.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,YAAY,MAAM,2CAA2C,CAAC;AACrE,OAAO,OAAO,MAAM,sCAAsC,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKrD,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU;IAEhB,UAAU,EAAE;QACV,YAAY;QACZ,OAAO;KACR;IAED,QAAQ,EAAE;QACR,QAAQ;YACN,MAAM,MAAM,GACV,IAAI,CAAC,kBAAkB,CAAC,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,IAAI,EAAE,CAAC;YAE7D,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<nav class="nav-links">
<!-- user links -->
<div v-for="item in navLinks" :key="item.link" class="nav-item">
<DropdownLink v-if="item.type === 'links'" :item="item" />
<NavLink v-else :item="item" />
</div>
</nav>
</template>
<script src="./NavLinks" />
<style lang="stylus">
.nav-links
display inline-block
.nav-item
position relative
display inline-block
line-height 2rem
margin-left 1.5rem
&:first-child
margin-left 0
> .nav-link
color var(--dark-grey)
&::after
position absolute
content ' '
left 50%
right 50%
bottom 0px
height 2px
background var(--accent-color-l10)
border-radius 1px
visibility hidden
transition left 0.2s ease-in-out, right 0.2s ease-in-out
&.active
color var(--accent-color)
&:hover, &.active
&::after
left 0
right 0
visibility visible
</style>
import Vue from "vue";
import type { AlgoliaOption } from "@mr-hope/vuepress-types";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
linksWrapMaxWidth: number;
isMobile: boolean;
}, unknown, {
siteBrandTitle: string;
canHideSiteBrandTitle: boolean;
siteBrandLogo: string;
siteBrandDarkLogo: string;
algoliaConfig: false | AlgoliaOption;
isAlgoliaSearch: boolean;
canHide: boolean;
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import AlgoliaSearchBox from "@AlgoliaSearchBox";
import LanguageDropdown from "@theme/components/Navbar/LanguageDropdown";
import NavLinks from "@theme/components/Navbar/NavLinks.vue";
import RepoLink from "@theme/components/Navbar/RepoLink.vue";
import SearchBox from "@SearchBox";
import SidebarButton from "@theme/components/Navbar/SidebarButton.vue";
import ThemeColor from "@ThemeColor";
let handler;
const css = (el, property) => {
// NOTE: Known bug, will return 'auto' if style value is 'auto'
const window = el.ownerDocument.defaultView;
// `null` means not to return pseudo styles
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return window.getComputedStyle(el, null)[property];
};
export default Vue.extend({
name: "Navbar",
components: {
AlgoliaSearchBox,
LanguageDropdown,
NavLinks,
RepoLink,
SearchBox,
SidebarButton,
ThemeColor,
},
data: () => ({
linksWrapMaxWidth: 0,
isMobile: false,
}),
computed: {
siteBrandTitle() {
return this.$site.title;
},
canHideSiteBrandTitle() {
return (Boolean(this.siteBrandTitle) &&
this.$themeConfig.hideSiteTitleonMobile !== false);
},
siteBrandLogo() {
const { logo } = this.$themeConfig;
return logo ? this.$withBase(logo) : "";
},
siteBrandDarkLogo() {
const { darkLogo } = this.$themeConfig;
return darkLogo ? this.$withBase(darkLogo) : "";
},
algoliaConfig() {
return (this.$themeLocaleConfig.algolia || this.$themeConfig.algolia || false);
},
isAlgoliaSearch() {
return Boolean(this.algoliaConfig &&
this.algoliaConfig.apiKey &&
this.algoliaConfig.indexName);
},
canHide() {
const autoHide = this.$themeConfig.navAutoHide;
return autoHide !== "none" && (autoHide === "always" || this.isMobile);
},
},
mounted() {
// Refer to config.styl
const MOBILE_DESKTOP_BREAKPOINT = 719;
const NAVBAR_HORIZONTAL_PADDING = parseInt(css(this.$el, "paddingLeft")) +
parseInt(css(this.$el, "paddingRight"));
handler = () => {
if (document.documentElement.clientWidth < MOBILE_DESKTOP_BREAKPOINT) {
this.isMobile = true;
this.linksWrapMaxWidth = 0;
}
else {
this.isMobile = false;
this.linksWrapMaxWidth =
this.$el.offsetWidth -
NAVBAR_HORIZONTAL_PADDING -
((this.$refs.siteInfo &&
this.$refs.siteInfo.$el &&
this.$refs.siteInfo.$el.offsetWidth) ||
0);
}
};
handler();
window.addEventListener("resize", handler);
window.addEventListener("orientationchange", handler);
},
// eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle
beforeDestroy() {
window.removeEventListener("resize", handler);
window.removeEventListener("orientationchange", handler);
},
});
//# sourceMappingURL=Navbar.js.map
\ No newline at end of file
{"version":3,"file":"Navbar.js","sourceRoot":"","sources":["Navbar.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<template>
<header class="navbar" :class="{ 'can-hide': canHide }">
<slot name="start" />
<SidebarButton @toggle-sidebar="$emit('toggle-sidebar')" />
<RouterLink ref="siteInfo" :to="$localePath" class="home-link">
<img
v-if="siteBrandLogo"
class="logo"
:class="{ light: Boolean(siteBrandDarkLogo) }"
:src="siteBrandLogo"
:alt="siteBrandTitle"
/>
<img
v-if="siteBrandDarkLogo"
class="logo dark"
:src="siteBrandDarkLogo"
:alt="siteBrandTitle"
/>
<span
v-if="siteBrandTitle"
class="site-name"
:class="{ 'can-hide': canHideSiteBrandTitle }"
>{{ siteBrandTitle }}</span
>
</RouterLink>
<slot name="center" />
<div
:style="
linksWrapMaxWidth ? { 'max-width': `${linksWrapMaxWidth}px` } : {}
"
class="links"
>
<ThemeColor />
<AlgoliaSearchBox v-if="isAlgoliaSearch" :options="algoliaConfig" />
<SearchBox
v-else-if="
$themeConfig.search !== false && $page.frontmatter.search !== false
"
/>
<NavLinks class="can-hide" />
<LanguageDropdown />
<RepoLink class="can-hide" />
<slot name="end" />
</div>
</header>
</template>
<script src="./Navbar" />
<style lang="stylus">
.navbar
position fixed
z-index 200
top 0
left 0
right 0
height $navbarHeight
padding $navbarVerticalPadding $navbarHorizontalPadding
background var(--bgcolor-blur)
box-sizing border-box
box-shadow 0 2px 8px var(--card-shadow-color)
backdrop-filter saturate(200%) blur(20px)
line-height: $navbarHeight - $navbarVerticalPadding * 2
transition transform 0.3s ease-in-out
@media (max-width $MQMedium)
height $navbarMobileHeight
padding $navbarMobileVerticalPadding $navbarMobileHorizontalPadding
padding-left: $navbarMobileHorizontalPadding + 2.4rem
line-height: $navbarMobileHeight - $navbarMobileVerticalPadding * 2
.hide-navbar &.can-hide
transform translateY(-100%)
a, span, img
display inline-block
.logo
min-width: 200px
height: $navbarHeight - $navbarVerticalPadding * 2
margin-right 0.8rem
vertical-align top
@media (max-width $MQMedium)
min-width: $navbarMobileHeight - $navbarMobileVerticalPadding * 2
height: $navbarMobileHeight - $navbarMobileVerticalPadding * 2
.theme-light &
&.light
display inline-block
&.dark
display none
.theme-dark &
&.light
display none
&.dark
display inline-block
.can-hide
@media (max-width $MQMedium)
display none
.site-name
font-size 1.5rem
color var(--text-color)
position relative
@media (max-width $MQMedium)
width calc(100vw - 9.4rem)
overflow hidden
white-space nowrap
text-overflow ellipsis
.links
position absolute
top $navbarVerticalPadding
right $navbarHorizontalPadding
display flex
box-sizing border-box
padding-left 1.5rem
font-size 0.9rem
white-space nowrap
@media (max-width $MQMedium)
padding-left 0
top $navbarMobileVerticalPadding
right $navbarMobileHorizontalPadding
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
repoLink: string;
repoLabel: string;
}, Record<never, any>>;
export default _default;
import Vue from "vue";
export default Vue.extend({
name: "RepoLink",
computed: {
repoLink() {
const { repo } = this.$themeConfig;
if (repo)
return /^https?:/u.test(repo) ? repo : `https://github.com/${repo}`;
return "";
},
repoLabel() {
if (!this.repoLink)
return "";
if (this.$themeConfig.repoLabel)
return this.$themeConfig.repoLabel;
const [repoHost] = /^https?:\/\/[^/]+/u.exec(this.repoLink) || [""];
const platforms = ["GitHub", "GitLab", "Bitbucket"];
for (let index = 0; index < platforms.length; index++) {
const platform = platforms[index];
if (new RegExp(platform, "iu").test(repoHost))
return platform;
}
return "Source";
},
},
});
//# sourceMappingURL=RepoLink.js.map
\ No newline at end of file
{"version":3,"file":"RepoLink.js","sourceRoot":"","sources":["RepoLink.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU;IAEhB,QAAQ,EAAE;QACR,QAAQ;YACN,MAAM,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;YAEnC,IAAI,IAAI;gBACN,OAAO,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAsB,IAAI,EAAE,CAAC;YAEtE,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,SAAS;YACP,IAAI,CAAC,IAAI,CAAC,QAAQ;gBAAE,OAAO,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS;gBAAE,OAAO,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC;YAEpE,MAAM,CAAC,QAAQ,CAAC,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpE,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;YAEpD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE;gBACrD,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;gBAElC,IAAI,IAAI,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAAE,OAAO,QAAQ,CAAC;aAChE;YAED,OAAO,QAAQ,CAAC;QAClB,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<!-- repo link -->
<a
v-if="repoLink && $themeConfig.repoDisplay !== false"
class="repo-link"
rel="noopener noreferrer"
:href="repoLink"
target="_blank"
>
{{ repoLabel }}
<OutboundLink />
</a>
</template>
<script src="./RepoLink" />
<style lang="stylus">
.repo-link
.navbar &
color var(--dark-grey)
margin-left 1rem
.sidebar-nav-links &
display block
padding 0.5rem 0 0.5rem 1.5rem
font-size 1.1em
line-height 1.25rem
</style>
<template>
<button
class="sidebar-button"
title="Sidebar Button"
@click="$emit('toggle-sidebar')"
>
<span class="icon" />
</button>
</template>
<style lang="stylus">
@require '~@mr-hope/vuepress-shared/styles/reset'
.sidebar-button
button()
display none
box-sizing content-box
position absolute
top calc(50% - 1.075rem)
left 1rem
width 1.25rem
height 1.25rem
padding 0.45rem
font unset
vertical-align middle
transition transform 0.2s ease-in-out
&::before
content ' '
margin-top 0.125em
&::after
content ' '
margin-bottom 0.125em
.icon
margin 0.2em 0
&::before, &::after, .icon
display block
width 100%
height 0.2em
transition transform 0.2s ease-in-out
border-radius 0.05em
background var(--text-color)
.sidebar-open .sidebar-button
&::before
transform translateY(0.4em) rotate(135deg)
.icon
transform scale(0)
&::after
transform translateY(-0.4em) rotate(-135deg)
@media (max-width $MQMedium)
.sidebar-button
display block
</style>
import Anchor from "@theme/components/Anchor.vue";
import type { PageHeader } from "@mr-hope/vuepress-types";
import type { SidebarItem } from "@theme/utils/sidebar";
declare const _default: import("vue/types/vue").ExtendedVue<{
encryptPasswordConfig: Record<string, string>;
} & {
checkPathPassword(password: string): void;
} & {
pathEncryptMatchKeys: string[];
isPathEncrypted: boolean;
} & Record<never, any> & {
encryptOptions: import("../types").EncryptOptions;
} & Anchor, {
password: string;
}, unknown, {
pagePassword: string;
pageDescrypted: boolean;
}, {
sidebarItems: SidebarItem[];
headers: PageHeader[];
}>;
export default _default;
import Anchor from "@theme/components/Anchor.vue";
import Comment from "@Comment";
import MyTransition from "@theme/components/MyTransition.vue";
import PageInfo from "@mr-hope/vuepress-plugin-comment/lib/client/PageInfo.vue";
import PageMeta from "@theme/components/PageMeta.vue";
import PageNav from "@theme/components/PageNav.vue";
import Password from "@theme/components/Password.vue";
import { pathEncryptMixin } from "@theme/mixins/pathEncrypt";
export default pathEncryptMixin.extend({
name: "Page",
components: {
Anchor,
Comment,
MyTransition,
PageInfo,
PageMeta,
PageNav,
Password,
},
props: {
sidebarItems: {
type: Array,
default: () => [],
},
headers: {
type: Array,
default: () => [],
},
},
data: () => ({
password: "",
}),
computed: {
pagePassword() {
const { password } = this.$frontmatter;
return typeof password === "number"
? password.toString()
: typeof password === "string"
? password
: "";
},
pageDescrypted() {
return this.password === this.pagePassword;
},
},
});
//# sourceMappingURL=Page.js.map
\ No newline at end of file
{"version":3,"file":"Page.js","sourceRoot":"","sources":["Page.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,8BAA8B,CAAC;AAClD,OAAO,OAAO,MAAM,UAAU,CAAC;AAC/B,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAC9D,OAAO,QAAQ,MAAM,0DAA0D,CAAC;AAChF,OAAO,QAAQ,MAAM,gCAAgC,CAAC;AACtD,OAAO,OAAO,MAAM,+BAA+B,CAAC;AACpD,OAAO,QAAQ,MAAM,gCAAgC,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAM7D,eAAe,gBAAgB,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE,MAAM;IAEZ,UAAU,EAAE;QACV,MAAM;QACN,OAAO;QACP,YAAY;QACZ,QAAQ;QACR,QAAQ;QACR,OAAO;QACP,QAAQ;KACT;IAED,KAAK,EAAE;QACL,YAAY,EAAE;YACZ,IAAI,EAAE,KAAgC;YACtC,OAAO,EAAE,GAAkB,EAAE,CAAC,EAAE;SACjC;QACD,OAAO,EAAE;YACP,IAAI,EAAE,KAA+B;YACrC,OAAO,EAAE,GAAiB,EAAE,CAAC,EAAE;SAChC;KACF;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,QAAQ,EAAE,EAAE;KACb,CAAC;IAEF,QAAQ,EAAE;QACR,YAAY;YACV,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;YAEvC,OAAO,OAAO,QAAQ,KAAK,QAAQ;gBACjC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE;gBACrB,CAAC,CAAC,OAAO,QAAQ,KAAK,QAAQ;oBAC9B,CAAC,CAAC,QAAQ;oBACV,CAAC,CAAC,EAAE,CAAC;QACT,CAAC;QAED,cAAc;YACZ,OAAO,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY,CAAC;QAC7C,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<main class="page">
<slot name="top" />
<BreadCrumb :key="$route.path" />
<PageInfo :key="$route.path" />
<MyTransition v-if="pagePassword && !pageDescrypted" :delay="0.08" :disable="true">
<Password
:key="$route.path"
:page="true"
@password-verify="password = $event"
/>
</MyTransition>
<MyTransition v-else-if="isPathEncrypted" :delay="0.08" :disable="true">
<Password
:key="$route.path"
:page="true"
@password-verify="checkPathPassword"
/>
</MyTransition>
<template v-else>
<MyTransition :delay="0.12" :disable="true">
<Anchor :key="$route.path" />
</MyTransition>
<slot v-if="!pagePassword || pageDescrypted" name="content-top" />
<MyTransition v-show="!pagePassword || pageDescrypted" :delay="0.08" :disable="true">
<Content :key="$route.path" class="theme-default-content" />
</MyTransition>
<slot v-if="!pagePassword || pageDescrypted" name="content-bottom" />
<MyTransition :delay="0.12" :disable="true">
<PageMeta :key="$route.path" />
</MyTransition>
<MyTransition :delay="0.14" :disable="true">
<PageNav :key="$route.path" v-bind="{ sidebarItems }" />
</MyTransition>
<MyTransition :delay="0.16" :disable="true">
<Comment :key="$route.path" />
</MyTransition>
</template>
<slot name="bottom" />
<!-- Google tag (gtag.js) -->
<!-- put here because the plugin didn't work -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-9KLVB8X0ME">
</script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-9KLVB8X0ME');
</script>
</main>
</template>
<script src="./Page" />
<style lang="stylus">
.page
display block
box-sizing border-box
min-height 100vh
padding-left $sidebarWidth
padding-bottom 2rem
background var(--bgcolor)
@media (max-width $MQMobile)
min-height 100vh
// narrow desktop / iPad
@media (max-width $MQNarrow)
padding-left $mobileSidebarWidth
// wide mobile
@media (max-width $MQMobile)
padding-left 0
@media (min-width $MQMobile)
.theme-container:not(.has-sidebar) &
padding-left 0
@media (min-width $MQWide)
.has-anchor &:not(.blog)
padding-right 16rem
</style>
import Vue from "vue";
import type { HopeFooterConfig } from "../types";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
footerConfig: HopeFooterConfig;
enable: boolean;
footerContent: string | false;
copyright: string | false;
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import MediaLinks from "@theme/components/MediaLinks.vue";
export default Vue.extend({
name: "PageFooter",
components: { MediaLinks },
computed: {
footerConfig() {
return this.$themeLocaleConfig.footer || this.$themeConfig.footer || {};
},
enable() {
const { copyrightText, footer, medialink } = this.$page.frontmatter;
return (footer !== false &&
Boolean(copyrightText || footer || medialink || this.footerConfig.display));
},
footerContent() {
const { footer } = this.$page.frontmatter;
return footer === false
? false
: typeof footer === "string"
? footer
: this.footerConfig.content || "";
},
copyright() {
return this.$frontmatter.copyrightText === false
? false
: this.$frontmatter.copyrightText ||
(this.footerConfig.copyright === false
? false
: this.footerConfig.copyright ||
(this.$themeConfig.author
? `Copyright © ${new Date().getFullYear()} ${this.$themeConfig.author}`
: ""));
},
},
});
//# sourceMappingURL=PageFooter.js.map
\ No newline at end of file
{"version":3,"file":"PageFooter.js","sourceRoot":"","sources":["PageFooter.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,UAAU,MAAM,kCAAkC,CAAC;AAI1D,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,YAAY;IAElB,UAAU,EAAE,EAAE,UAAU,EAAE;IAE1B,QAAQ,EAAE;QACR,YAAY;YACV,OAAO,IAAI,CAAC,kBAAkB,CAAC,MAAM,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,EAAE,CAAC;QAC1E,CAAC;QAED,MAAM;YACJ,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;YAEpE,OAAO,CACL,MAAM,KAAK,KAAK;gBAChB,OAAO,CACL,aAAa,IAAI,MAAM,IAAI,SAAS,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,CAClE,CACF,CAAC;QACJ,CAAC;QAED,aAAa;YACX,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;YAE1C,OAAO,MAAM,KAAK,KAAK;gBACrB,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,OAAO,MAAM,KAAK,QAAQ;oBAC5B,CAAC,CAAC,MAAM;oBACR,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,IAAI,EAAE,CAAC;QACtC,CAAC;QAED,SAAS;YACP,OAAO,IAAI,CAAC,YAAY,CAAC,aAAa,KAAK,KAAK;gBAC9C,CAAC,CAAC,KAAK;gBACP,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,aAAa;oBAC7B,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,KAAK,KAAK;wBACpC,CAAC,CAAC,KAAK;wBACP,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS;4BAC3B,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM;gCACvB,CAAC,CAAC,eAAe,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IACrC,IAAI,CAAC,YAAY,CAAC,MACpB,EAAE;gCACJ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrB,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<footer v-if="enable" class="footer-wrapper">
<MediaLinks v-if="!($frontmatter.home && $frontmatter.blog)" />
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="footerContent" class="footer" v-html="footerContent" />
<!-- eslint-disable-next-line vue/no-v-html -->
<div v-if="copyright" class="copyright" v-html="copyright" />
</footer>
</template>
<script src="./PageFooter" />
<style lang="stylus">
.footer-wrapper
display flex
flex-wrap wrap
justify-content space-evenly
align-items center
padding 12px 30px
border-top 1px solid var(--border-color)
background var(--bgcolor)
color var(--dark-color, #666)
text-align center
@media (min-width $MQMobile)
.has-sidebar &
padding-left $sidebarWidth
border-left 30px solid transparent
& > div
@media (max-width $MQMobileNarrow)
width 100%
.media-links-wrapper
margin 0
.footer
margin 8px 16px
font-size 14px
.copyright
margin 6px 0
font-size 13px
.page:not(.not-found) + .footer-wrapper
margin-top -2rem
</style>
import Vue from "vue";
import type { GitContributor } from "@mr-hope/vuepress-plugin-git";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
createEditLink(): string;
}, {
i18n: {
contributor: string;
editLink: string;
updateTime: string;
};
contributors: GitContributor[];
contributorsText: string;
updateTime: string;
updateTimeText: string;
editLink: string | false;
editLinkText: string;
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import EditIcon from "@theme/icons/EditIcon.vue";
import { endingSlashRE, outboundRE } from "@theme/utils/path";
export default Vue.extend({
name: "PageMeta",
components: { EditIcon },
computed: {
i18n() {
return (this.$themeLocaleConfig.meta || {
contributor: "Contributors",
editLink: "Edit this page",
updateTime: "Last Updated",
});
},
contributors() {
return this.$page.frontmatter.contributor === false ||
(this.$themeConfig.contributor === false &&
!this.$page.frontmatter.contributor)
? []
: this.$page.contributors || [];
},
contributorsText() {
return this.i18n.contributor;
},
updateTime() {
return this.$page.frontmatter.contributor === false ||
(this.$themeConfig.updateTime === false &&
!this.$page.frontmatter.updateTime)
? ""
: this.$page.updateTime || "";
},
updateTimeText() {
return this.i18n.updateTime;
},
editLink() {
const showEditLink = this.$page.frontmatter.editLink ||
(this.$themeConfig.editLinks !== false &&
this.$page.frontmatter.editLink !== false);
const { repo, docsRepo } = this.$site.themeConfig;
if (showEditLink && (repo || docsRepo) && this.$page.relativePath)
return this.createEditLink();
return false;
},
editLinkText() {
return this.i18n.editLink;
},
},
methods: {
createEditLink() {
const { repo = "", docsRepo = repo, docsDir = "", docsBranch = "main", } = this.$themeConfig;
const bitbucket = /bitbucket.org/u;
if (bitbucket.test(docsRepo))
return `${docsRepo.replace(endingSlashRE, "")}/src/${docsBranch}/${docsDir ? `${docsDir.replace(endingSlashRE, "")}/` : ""}${this.$page.relativePath}?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`;
const gitlab = /gitlab.com/u;
if (gitlab.test(docsRepo))
return `${docsRepo.replace(endingSlashRE, "")}/-/edit/${docsBranch}/${docsDir ? `${docsDir.replace(endingSlashRE, "")}/` : ""}${this.$page.relativePath}`;
const base = outboundRE.test(docsRepo)
? docsRepo
: `https://github.com/${docsRepo}`;
return `${base.replace(endingSlashRE, "")}/edit/${docsBranch}/${docsDir ? `${docsDir.replace(endingSlashRE, "")}/` : ""}${this.$page.relativePath}`;
},
},
});
//# sourceMappingURL=PageMeta.js.map
\ No newline at end of file
{"version":3,"file":"PageMeta.js","sourceRoot":"","sources":["PageMeta.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<template>
<footer class="page-meta">
<div class="footer-box">
<div class="footer-box-area">
<span class="footer-section-header">About this page</span>
<ul>
<li>Updated: {{ updateTime.slice(0, updateTime.length - 6) }}</li>
</ul>
</div>
<div class="footer-box-area">
<span class="footer-section-header">Contribute</span>
<ul>
<li>
<a :href="editLink" target="_blank" rel="noopener noreferrer">
<i class="far fa-pencil"></i> Edit this page
</a>
</li>
<li>
<a href="https://github.com/ethereum-optimism/optimism/contribute" target="_blank" rel="noopener noreferrer">
<i class="far fa-hands-helping"></i> Contribute to Optimism
</a>
</li>
</ul>
</div>
<div class="footer-box-area">
<span class="footer-section-header">Still need help?</span>
<ul>
<li>
<a href="https://discord.optimism.io" target="_blank" rel="noopener noreferrer">
<i class="fab fa-discord"></i> Discord community
</a>
</li>
<li>
<a href="https://forms.monday.com/forms/c867f3f357707ff1fb4af0d3d5080710?r=use1" target="_blank" rel="noopener noreferrer">
<i class="far fa-comment-dots"></i> Get support for going live
</a>
</li>
</ul>
</div>
</div>
</footer>
</template>
<script src="./PageMeta" />
<style lang="stylus">
@require '~@mr-hope/vuepress-shared/styles/wrapper'
.page-meta
@extend $wrapper
padding-top 12px
padding-bottom 12px
font-family Arial, Helvetica, sans-serif
overflow auto
.meta-item
.label
font-weight 500
color var(--text-color-l25)
.info
font-weight 400
color var(--dark-grey)
.edit-link
display inline-block
font-size 14px
.icon
position relative
bottom -0.125em
width 1em
height 1em
color var(--accent-color)
@media (max-width $MQMobile)
margin-bottom 8px
a
color var(--accent-color-l10)
.update-time
float right
font-size 14px
@media (max-width $MQMobile)
float none
font-size 13px
text-align left
.contributors
font-size 14px
text-align right
@media (max-width $MQMobile)
font-size 13px
text-align left
.footer-box
display flex
flex-direction row
justify-content space-between
background-color #F1F4F9
padding 32px
padding-left: 40px;
padding-right: 40px;
border-radius 16px
@media (max-width $MQNarrow)
flex-direction column
.footer-box-area
@media (max-width $MQNarrow)
margin-bottom 32px
span.footer-section-header
font-family 'Open Sans', sans-serif
font-weight 600
font-size 14px
line-height 20px
ul
list-style-type none
padding-left 0
font-size 14px
line-height 20px
margin-top 10px
margin-bottom 0px
color #68778D
li
margin-top 15px
margin-bottom 5px
a
color #68778D
font-family 'Open Sans', sans-serif
&:hover
color #FF0420
i
font-size 14px
width 20px
margin-right 3px
text-align center
</style>
import Vue from "vue";
import type { SidebarItem } from "@theme/utils/sidebar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
prev: false | SidebarItem;
next: false | SidebarItem;
}, {
sidebarItems: SidebarItem[];
}>;
export default _default;
import Vue from "vue";
import NextIcon from "@theme/icons/NextIcon.vue";
import PrevIcon from "@theme/icons/PrevIcon.vue";
import { resolvePath } from "@theme/utils/path";
import { resolvePageforSidebar } from "@theme/utils/sidebar";
const getSidebarItems = (items, result) => {
for (const item of items)
if (item.type === "group")
getSidebarItems((item.children || []), result);
else
result.push(item);
};
const find = (page, items, offset) => {
const result = [];
getSidebarItems(items, result);
for (let i = 0; i < result.length; i++) {
const cur = result[i];
if (cur.type === "page" && cur.path === decodeURIComponent(page.path))
return result[i + offset];
}
return false;
};
const resolvePageLink = (linkType, { themeConfig, page, route, site, sidebarItems }) => {
const themeLinkConfig =
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
themeConfig[`${linkType}Links`];
const pageLinkConfig = page.frontmatter[linkType];
if (themeLinkConfig === false || pageLinkConfig === false)
return false;
if (typeof pageLinkConfig === "string")
return resolvePageforSidebar(site.pages, resolvePath(pageLinkConfig, route.path));
return find(page, sidebarItems, linkType === "prev" ? -1 : 1);
};
export default Vue.extend({
name: "PageNav",
components: { NextIcon, PrevIcon },
props: {
sidebarItems: {
type: Array,
default: () => [],
},
},
computed: {
prev() {
return resolvePageLink("prev", {
sidebarItems: this.sidebarItems,
themeConfig: this.$themeConfig,
page: this.$page,
route: this.$route,
site: this.$site,
});
},
next() {
return resolvePageLink("next", {
sidebarItems: this.sidebarItems,
themeConfig: this.$themeConfig,
page: this.$page,
route: this.$route,
site: this.$site,
});
},
},
});
//# sourceMappingURL=PageNav.js.map
\ No newline at end of file
{"version":3,"file":"PageNav.js","sourceRoot":"","sources":["PageNav.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<template>
<div v-if="prev || next" class="page-nav">
<p class="inner">
<span v-if="prev" class="prev">
<a
v-if="prev.type === 'external'"
class="prev"
:href="prev.path"
target="_blank"
rel="noopener noreferrer"
>
<PrevIcon />
{{ prev.title || prev.path }}
<OutboundLink />
</a>
<RouterLink v-else class="prev" :to="prev.path">
<PrevIcon />
{{ prev.title || prev.path }}
</RouterLink>
</span>
<span v-if="next" class="next">
<a
v-if="next.type === 'external'"
:href="next.path"
target="_blank"
rel="noopener noreferrer"
>
{{ next.title || next.path }}
<OutboundLink />
<NextIcon />
</a>
<RouterLink v-else :to="next.path">
{{ next.title || next.path }}
<NextIcon />
</RouterLink>
</span>
</p>
</div>
</template>
<script src="./PageNav" />
<style lang="stylus">
@require '~@mr-hope/vuepress-shared/styles/wrapper'
.page-nav
@extend $wrapper
padding-top 12px
padding-bottom 0
font-family Arial, Helvetica, sans-serif
.inner
min-height 32px
margin-top 0
border-top 1px solid var(--border-color)
padding-top 16px
overflow auto // clear float
.prev .icon, .next .icon
position relative
top 0.125em
width 1em
height 1em
color var(--accent-color)
.next
float right
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
password: string;
hasTried: boolean;
}, {
verify(): void;
}, {
isMainPage: boolean;
encrypt: {
title: string;
errorHint: string;
};
}, {
page: boolean;
}>;
export default _default;
import { getDefaultLocale } from "@mr-hope/vuepress-shared";
import Vue from "vue";
export default Vue.extend({
name: "Password",
props: {
page: { type: Boolean, default: false },
},
data: () => ({
password: "",
hasTried: false,
}),
computed: {
isMainPage() {
return this.$frontmatter.home === true;
},
encrypt() {
return this.$themeLocaleConfig.encrypt || getDefaultLocale().encrypt;
}
},
methods: {
verify() {
this.hasTried = false;
// eslint-disable-next-line vue/require-explicit-emits
this.$emit("password-verify", this.password);
void Vue.nextTick().then(() => {
this.hasTried = true;
});
},
},
});
//# sourceMappingURL=Password.js.map
\ No newline at end of file
{"version":3,"file":"Password.js","sourceRoot":"","sources":["Password.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,GAAG,MAAM,KAAK,CAAC;AAItB,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU;IAEhB,KAAK,EAAE;QACL,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE;KACxC;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,QAAQ,EAAE,EAAE;QACZ,QAAQ,EAAE,KAAK;KAChB,CAAC;IAEF,QAAQ,EAAE;QACR,UAAU;YACR,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,KAAK,IAAI,CAAC;QACzC,CAAC;QAED,OAAO;YACL,OAAO,IAAI,CAAC,kBAAkB,CAAC,OAAO,IAAI,gBAAgB,EAAE,CAAC,OAAO,CAAC;QACvE,CAAC;KACF;IAED,OAAO,EAAE;QACP,MAAM;YACJ,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,sDAAsD;YACtD,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;YAE7C,KAAK,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACvB,CAAC,CAAC,CAAC;QACL,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="password" :class="{ expand: page || isMainPage }">
<div class="hint" :class="{ hasTried }">
{{ hasTried ? encrypt.errorHint : encrypt.title }}
</div>
<div class="input">
<input v-model="password" type="password" @keypress.enter="verify" />
<button @click="verify">OK</button>
</div>
</div>
</template>
<script src="./Password" />
<style lang="stylus">
.password
background var(--bg-color)
height 90vh - $navbarHeight
margin-top $navbarHeight
text-align center
padding-left $sidebarWidth
display flex
flex-direction column
justify-content center
align-items center
@media (max-width $MQNarrow)
height 90vh - $navbarMobileHeight
margin-top $navbarMobileHeight
padding-left $mobileSidebarWidth
@media (max-width $MQMobile)
padding-left 0
&.expand
padding-left 0 !important
margin-top 0
height 400px
.hint
margin-bottom 20px
font-family Arial, Helvetica, sans-serif
font-weight 600
font-size 22px
line-height 2
&.hasTried
color red
animation-name shake
animation-duration 0.5s
animation-timing-function ease-out
animation-fill-mode both
.input
width 80%
max-width 600px
display flex
justify-content center
input
flex 1
width calc(100% - 60px)
padding-left 20px
color var(--black) !important
background var(--bgcolor) !important
border 2px solid var(--accent-color)
border-radius 22px 0 0 22px
font-size 20px
letter-spacing 0.5em
line-height 2
outline none
button
width 70px
padding-right 10px
background var(--accent-color)
color var(--bgcolor)
border-width 0
border-radius 0 22px 22px 0
font-size 20px
line-height 2
outline none
&:hover
background lighten($accentColor, 15%)
@keyframes shake
0%, 100%
transform translateX(0)
10%
transform translateX(-9px)
20%
transform translateX(8px)
30%
transform translateX(-7px)
40%
transform translateX(6px)
50%
transform translateX(-5px)
60%
transform translateX(4px)
70%
transform translateX(-3px)
80%
transform translateX(2px)
90%
transform translateX(-1px)
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
setHeight(items: HTMLElement): void;
unsetHeight(items: HTMLElement): void;
}, unknown, Record<never, any>>;
export default _default;
import Vue from "vue";
export default Vue.extend({
name: "DropdownTransition",
methods: {
setHeight(items) {
// explicitly set height so that it can be transitioned
items.style.height = `${items.scrollHeight}px`;
},
unsetHeight(items) {
items.style.height = "";
},
},
});
//# sourceMappingURL=DropdownTransition.js.map
\ No newline at end of file
{"version":3,"file":"DropdownTransition.js","sourceRoot":"","sources":["DropdownTransition.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AAEtB,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,oBAAoB;IAE1B,OAAO,EAAE;QACP,SAAS,CAAC,KAAkB;YAC1B,uDAAuD;YACvD,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,IAAI,CAAC;QACjD,CAAC;QAED,WAAW,CAAC,KAAkB;YAC5B,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;QAC1B,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<transition
name="dropdown"
@enter="setHeight"
@after-enter="unsetHeight"
@before-leave="setHeight"
>
<slot />
</transition>
</template>
<script src="./DropdownTransition" />
<style lang="stylus">
.dropdown-enter, .dropdown-leave-to
height 0 !important
</style>
import Vue from "vue";
import type { BlogOptions } from "@theme/types";
import type { SidebarItem } from "@theme/utils/sidebar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
blogConfig: BlogOptions;
sidebarDisplay: "always" | "mobile" | "none";
}, {
items: SidebarItem[];
}>;
export default _default;
import Vue from "vue";
import BlogInfo from "@BlogInfo";
import BloggerInfo from "@BloggerInfo";
import SidebarNavLinks from "@theme/components/Sidebar/SidebarNavLinks.vue";
import SidebarLinks from "@theme/components/Sidebar/SidebarLinks.vue";
export default Vue.extend({
name: "Sidebar",
components: {
BlogInfo,
BloggerInfo,
SidebarLinks,
SidebarNavLinks,
},
props: {
items: { type: Array, required: true },
},
computed: {
blogConfig() {
return this.$themeConfig.blog || {};
},
sidebarDisplay() {
return this.blogConfig.sidebarDisplay || "none";
},
},
});
//# sourceMappingURL=Sidebar.js.map
\ No newline at end of file
{"version":3,"file":"Sidebar.js","sourceRoot":"","sources":["Sidebar.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,eAAe,MAAM,+CAA+C,CAAC;AAC5E,OAAO,YAAY,MAAM,4CAA4C,CAAC;AAMtE,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,SAAS;IAEf,UAAU,EAAE;QACV,QAAQ;QACR,WAAW;QACX,YAAY;QACZ,eAAe;KAChB;IAED,KAAK,EAAE;QACL,KAAK,EAAE,EAAE,IAAI,EAAE,KAAgC,EAAE,QAAQ,EAAE,IAAI,EAAE;KAClE;IAED,QAAQ,EAAE;QACR,UAAU;YACR,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC;QACtC,CAAC;QAED,cAAc;YACZ,OAAO,IAAI,CAAC,UAAU,CAAC,cAAc,IAAI,MAAM,CAAC;QAClD,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<aside class="sidebar">
<template v-if="sidebarDisplay !== 'none'">
<BloggerInfo :class="{ mobile: sidebarDisplay === 'mobile' }" />
<hr />
</template>
<slot name="top" />
<SidebarNavLinks />
<slot name="center" />
<SidebarLinks :depth="0" :items="items" />
<slot name="bottom" />
<BlogInfo v-if="$frontmatter.blog && $themeConfig.blog !== false" />
</aside>
</template>
<script src="./Sidebar" />
<style lang="stylus">
.sidebar
position fixed
z-index 150
top $navbarHeight
left 0
bottom 0
box-sizing border-box
width $sidebarWidth
margin 0
background var(--bgcolor-blur)
box-shadow 2px 0 4px var(--card-shadow-color)
backdrop-filter saturate(200%) blur(20px)
font-size 16px
overflow-y auto
@media (max-width $MQMedium)
top $navbarMobileHeight
.theme-container.hide-navbar &
top 0
.theme-container:not(.has-navbar) &
top 0
a
display inline-block
color var(--text-color)
.blogger-info.mobile
display none
.blogger-info.mobile + hr
display none
& > .sidebar-links
padding 1.5rem 0
& > li
& > a.sidebar-link
font-size 1.1em
line-height 1.7
&:not(:first-child)
margin-top 0.75rem
// narrow desktop / iPad
@media (max-width $MQNarrow)
width $mobileSidebarWidth
font-size 15px
@media (min-width $MQMedium)
.theme-container:not(.has-sidebar) &
display none
// wide mobile
@media (max-width $MQMedium)
transform translateX(-100%)
transition transform 0.2s ease
box-shadow none
.theme-container.sidebar-open &
transform translateX(0)
box-shadow 2px 0 8px var(--card-shadow-color)
.theme-container:not(.has-navbar) &
top 0
.blogger-info.mobile
display block
.blogger-info.mobile + hr
display block
margin-top 16px
& > .sidebar-links
padding 1rem 0
</style>
import Vue from "vue";
import type { NavBarConfigItem } from "@theme/utils/navbar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
open: boolean;
}, {
setOpen(value: boolean): void;
isLastItemOfArray(item: NavBarConfigItem, array: NavBarConfigItem[]): boolean;
}, {
dropdownAriaLabel: string;
iconPrefix: string;
}, {
item: NavBarConfigItem;
}>;
export default _default;
import Vue from "vue";
import DropdownTransition from "@theme/components/Sidebar/DropdownTransition.vue";
import NavLink from "@theme/components/Navbar/NavLink.vue";
export default Vue.extend({
name: "SidebarDropdownLink",
components: { NavLink, DropdownTransition },
props: {
item: { type: Object, required: true },
},
data: () => ({
open: false,
}),
computed: {
dropdownAriaLabel() {
return this.item.ariaLabel || this.item.text;
},
iconPrefix() {
const { iconPrefix } = this.$themeConfig;
return iconPrefix === "" ? "" : iconPrefix || "icon-";
},
},
watch: {
$route() {
this.open = false;
},
},
methods: {
setOpen(value) {
this.open = value;
},
isLastItemOfArray(item, array) {
if (Array.isArray(array))
return item === array[array.length - 1];
return false;
},
},
});
//# sourceMappingURL=SidebarDropdownLink.js.map
\ No newline at end of file
{"version":3,"file":"SidebarDropdownLink.js","sourceRoot":"","sources":["SidebarDropdownLink.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,kBAAkB,MAAM,kDAAkD,CAAC;AAClF,OAAO,OAAO,MAAM,sCAAsC,CAAC;AAK3D,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,qBAAqB;IAE3B,UAAU,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE;IAE3C,KAAK,EAAE;QACL,IAAI,EAAE,EAAE,IAAI,EAAE,MAAoC,EAAE,QAAQ,EAAE,IAAI,EAAE;KACrE;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,IAAI,EAAE,KAAK;KACZ,CAAC;IAEF,QAAQ,EAAE;QACR,iBAAiB;YACf,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAC/C,CAAC;QAED,UAAU;YACR,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;YAEzC,OAAO,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,OAAO,CAAC;QACxD,CAAC;KACF;IAED,KAAK,EAAE;QACL,MAAM;YACJ,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QACpB,CAAC;KACF;IAED,OAAO,EAAE;QACP,OAAO,CAAC,KAAc;YACpB,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;QACpB,CAAC;QAED,iBAAiB,CACf,IAAsB,EACtB,KAAyB;YAEzB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,KAAK,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAElE,OAAO,KAAK,CAAC;QACf,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="mobile-dropdown-wrapper" :class="{ open }">
<button
class="dropdown-title"
type="button"
:aria-label="dropdownAriaLabel"
@click="setOpen(!open)"
>
<span class="title">
<i v-if="item.icon" :class="`iconfont ${iconPrefix}${item.icon}`" />
{{ item.text }}
</span>
<span class="arrow" :class="open ? 'down' : 'right'" />
</button>
<DropdownTransition>
<ul v-show="open" class="nav-dropdown">
<li
v-for="(child, index) in item.items"
:key="child.link || index"
class="dropdown-item"
>
<h4 v-if="child.type === 'links'" class="dropdown-subtitle">
<NavLink
v-if="child.link"
:item="child"
@focusout="
isLastItemOfArray(child, item.children) &&
child.children.length === 0 &&
setOpen(false)
"
/>
<span v-else>{{ child.text }}</span>
</h4>
<ul v-if="child.type === 'links'" class="dropdown-subitem-wrapper">
<li
v-for="grandchild in child.items"
:key="grandchild.link"
class="dropdown-subitem"
>
<NavLink
:item="grandchild"
@focusout="
isLastItemOfArray(grandchild, child.items) &&
isLastItemOfArray(child, item.items) &&
setOpen(false)
"
/>
</li>
</ul>
<NavLink
v-else
:item="child"
@focusout="isLastItemOfArray(child, item.items) && setOpen(false)"
/>
</li>
</ul>
</DropdownTransition>
</div>
</template>
<script src="./SidebarDropdownLink" />
<style lang="stylus">
@require '~@mr-hope/vuepress-shared/styles/arrow'
@require '~@mr-hope/vuepress-shared/styles/reset'
.mobile-dropdown-wrapper
cursor pointer
.dropdown-title
button()
cursor inherit
padding inherit
color var(--text-color)
font-family inherit
font-size inherit
line-height 1.4rem
&:hover
color var(--accent-color)
.arrow
arrow()
.nav-dropdown
margin-top 0.25rem
transition height 0.1s ease-out
overflow hidden
.dropdown-item
color inherit
line-height 1.7rem
h4
margin 0
padding-left 1.25rem
font-size 15px
line-height 1.7
.nav-link
padding 0
&:before
display none
.nav-link
display block
position relative
margin-bottom 0
padding 0 1.5rem 0 1.25rem
border-bottom none
font-weight 400
line-height 1.7rem
&:hover
color var(--accent-color)
&.active
color var(--accent-color)
&::before
content ''
position absolute
top calc(50% - 3px)
left 9px
width 0
height 0
border-top 3px solid transparent
border-left 5px solid var(--accent-color)
border-bottom 3px solid transparent
& > .nav-link
font-size 15px
line-height 2rem
.dropdown-subitem-wrapper
padding 0
list-style none
.dropdown-subitem
font-size 0.9em
padding-left 0.5rem
</style>
import Vue from "vue";
import type { SidebarAutoItem, SidebarGroupItem } from "@theme/utils/sidebar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
getIcon(icon: string | undefined): string;
isActive: (route: import("vue-router").Route, path: string) => boolean;
}, unknown, {
item: SidebarAutoItem | SidebarGroupItem;
open: boolean;
depth: number;
}>;
export default _default;
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import Vue from "vue";
import DropdownTransition from "@theme/components/Sidebar/DropdownTransition.vue";
import { isActive } from "@theme/utils/path";
export default Vue.extend({
name: "SidebarGroup",
components: { DropdownTransition },
props: {
item: {
type: Object,
required: true,
},
open: { type: Boolean },
depth: { type: Number, required: true },
},
beforeCreate() {
// eslint-disable-next-line
this.$options.components.SidebarLinks =
// eslint-disable-next-line @typescript-eslint/no-var-requires
require("@theme/components/Sidebar/SidebarLinks.vue").default;
},
methods: {
getIcon(icon) {
const { iconPrefix } = this.$themeConfig;
return this.$themeConfig.sidebarIcon !== false && icon
? `${iconPrefix === "" ? "" : iconPrefix || "icon-"}${icon}`
: "";
},
isActive,
},
});
//# sourceMappingURL=SidebarGroup.js.map
\ No newline at end of file
{"version":3,"file":"SidebarGroup.js","sourceRoot":"","sources":["SidebarGroup.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,kBAAkB,MAAM,kDAAkD,CAAC;AAClF,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAK7C,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,cAAc;IAEpB,UAAU,EAAE,EAAE,kBAAkB,EAAE;IAElC,KAAK,EAAE;QACL,IAAI,EAAE;YACJ,IAAI,EAAE,MAAsD;YAC5D,QAAQ,EAAE,IAAI;SACf;QACD,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE;QACvB,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;KACxC;IAED,YAAY;QACV,2BAA2B;QAC3B,IAAI,CAAC,QAAQ,CAAC,UAAW,CAAC,YAAY;YACpC,8DAA8D;YAC9D,OAAO,CAAC,4CAA4C,CAAC,CAAC,OAAO,CAAC;IAClE,CAAC;IAED,OAAO,EAAE;QACP,OAAO,CAAC,IAAwB;YAC9B,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC;YAEzC,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,KAAK,KAAK,IAAI,IAAI;gBACpD,CAAC,CAAC,GAAG,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,IAAI,OAAO,GAAG,IAAI,EAAE;gBAC5D,CAAC,CAAC,EAAE,CAAC;QACT,CAAC;QAED,QAAQ;KACT;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<section
:class="[
{
collapsable: item.collapsable,
'is-sub-group': depth !== 0,
},
`depth-${depth}`,
]"
class="sidebar-group"
>
<RouterLink
v-if="item.path"
:class="{
open,
active: isActive($route, item.path),
}"
class="sidebar-heading clickable"
:to="item.path"
@click="$emit('toggle')"
>
<i v-if="item.icon" :class="`iconfont ${getIcon(item.icon)}`" />
<span class="title">{{ item.title }}</span>
<span
v-if="item.collapsable"
:class="open ? 'down' : 'right'"
class="arrow"
/>
</RouterLink>
<p
v-else
:class="{ clickable: item.collapsable, open }"
class="sidebar-heading"
@click="$emit('toggle')"
>
<i v-if="item.icon" :class="`iconfont ${getIcon(item.icon)}`" />
<span class="title">{{ item.title }}</span>
<span
v-if="item.collapsable"
:class="open ? 'down' : 'right'"
class="arrow"
/>
</p>
<DropdownTransition>
<SidebarLinks
v-if="open || !item.collapsable"
class="sidebar-group-items"
:depth="depth + 1"
:items="item.children"
/>
</DropdownTransition>
</section>
</template>
<script src="./SidebarGroup" />
<style lang="stylus">
@require '~@mr-hope/vuepress-shared/styles/arrow'
.sidebar-group
.sidebar-group
padding-left 0.5em
&:not(.collapsable)
.sidebar-heading:not(.clickable)
color inherit
cursor auto
// refine styles of nested sidebar groups
&.is-sub-group
padding-left 0
& > .sidebar-heading
padding-left 1.75rem
font-weight normal
line-height 1.5
&:not(.clickable)
opacity 0.8
& > .sidebar-group-items
padding-left 1rem
& > li > .sidebar-link
border-left none
font-size 0.95em
&.depth-2
& > .sidebar-heading
border-left none
.sidebar-heading
display flex
box-sizing border-box
width 100%
margin 0
padding 0.35rem 1rem 0.35rem 1.25rem
border-left 0.25rem solid transparent
border-top-right-radius 0.25rem
border-bottom-right-radius 0.25rem
color var(--text-color)
font-size 1.1em
cursor pointer
transition color 0.15s ease
user-select none
&.open
color inherit
&.clickable
&:hover
background-color var(--bgcolor-light)
&.active
border-left-color var(--accent-color)
color var(--accent-color)
font-weight 500
.iconfont
margin-right 0.25em
.title
flex 1
.arrow
arrow()
font-size 1.5em
float right
.sidebar-group-items
font-size 0.95em
transition height 0.1s ease-out
overflow hidden
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, unknown, Record<never, any>>;
export default _default;
import Vue from "vue";
import { hashRE, isActive } from "@theme/utils/path";
import { groupSidebarHeaders } from "@theme/utils/sidebar";
const renderIcon = (h, icon) => icon
? h("i", {
class: ["iconfont", icon],
})
: null;
const renderLink = (h, { icon = "", text, link, level, active }) => h("RouterLink", {
props: {
to: link,
activeClass: "",
exactActiveClass: "",
},
class: {
active,
"sidebar-link": true,
[level ? `heading${level}` : ""]: level && level !== 2,
},
}, [renderIcon(h, icon), text]);
const renderExternalLink = (h, { path, title = path }) => h("a", {
attrs: {
href: path,
target: "_blank",
rel: "noopener noreferrer",
},
class: { "sidebar-link": true },
}, [title, h("OutboundLink")]);
const renderChildren = (h, { children, path, route, maxDepth, depth = 1 }) => {
if (!children || depth > maxDepth)
return null;
return h("ul", { class: "sidebar-sub-headers" }, children.map((child) => {
const active = isActive(route, `${path}#${child.slug}`);
return h("li", { class: "sidebar-sub-header" }, [
renderLink(h, {
text: child.title,
link: `${path}#${child.slug}`,
level: child.level,
active,
}),
renderChildren(h, {
children: child.children || false,
path,
route,
maxDepth,
depth: depth + 1,
}),
]);
}));
};
export default Vue.extend({
name: "SidebarLink",
functional: true,
props: {
item: {
type: Object,
required: true,
},
},
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
render(h, { parent: { $page, $route, $themeConfig, $themeLocaleConfig }, props }) {
const { item } = props;
// if the item can not be resolved
if (item.type === "error")
return null;
// external link
if (item.type === "external")
return renderExternalLink(h, item);
/*
* Use custom active class matching logic
* Due to edge case of paths ending with / + hash
*/
const selfActive = isActive($route, item.path);
/** whether the item is active */
const active =
// if the item is a heading, then one of the children needs to be active
item.type === "header"
? selfActive ||
(item.children || []).some((child) => isActive($route, `${item.basePath}#${child.slug}`))
: selfActive;
const pageMaxDepth = $page.frontmatter.sidebarDepth;
const localesMaxDepth = $themeLocaleConfig.sidebarDepth;
const themeMaxDepth = $themeConfig.sidebarDepth;
const maxDepth = typeof pageMaxDepth === "number"
? pageMaxDepth
: typeof localesMaxDepth === "number"
? localesMaxDepth
: typeof themeMaxDepth === "number"
? themeMaxDepth
: 2;
// the item is a heading
if (item.type === "header")
return [
renderLink(h, {
text: item.title || item.path,
link: item.path,
level: item.level,
active,
}),
renderChildren(h, {
children: item.children || false,
path: item.basePath,
route: $route,
maxDepth,
}),
];
const displayAllHeaders = $themeLocaleConfig.displayAllHeaders ||
$themeConfig.displayAllHeaders;
const link = renderLink(h, {
icon: $themeConfig.sidebarIcon !== false && item.frontmatter.icon
? `${$themeConfig.iconPrefix === ""
? ""
: $themeConfig.iconPrefix || "icon-"}${item.frontmatter.icon}`
: "",
text: item.title || item.path,
link: item.path,
active,
});
if ((active || displayAllHeaders) &&
item.headers &&
!hashRE.test(item.path)) {
const children = groupSidebarHeaders(item.headers);
return [
link,
renderChildren(h, {
children,
path: item.path,
route: $route,
maxDepth,
}),
];
}
return link;
},
});
//# sourceMappingURL=SidebarLink.js.map
\ No newline at end of file
{"version":3,"file":"SidebarLink.js","sourceRoot":"","sources":["SidebarLink.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<script src="./SidebarLink" />
<style lang="stylus">
$headings = 3 4 5 6
.sidebar-links
ul
padding 0
margin 0
list-style-type none
&.sidebar-sub-headers
padding-left 1rem
font-size 0.95em
.has-anchor &
@media (min-width $MQWide)
display none
a.sidebar-link
display inline-block
box-sizing border-box
width 100%
padding 0.35rem 1rem 0.35rem 1.25rem
border-left 0.2rem solid transparent
color var(--text-color)
font-size 1em
line-height 1.5
font-weight 400
for $heading in $headings
&.heading{$heading}
padding-left ($heading - 0.5) * 0.5rem !important
.iconfont
margin-right 0.25em
&:hover
background-color var(--bgcolor-light)
&.active
border-left-color var(--accent-color)
background-color var(--accent-color-a15)
color var(--accent-color)
font-weight 500
.iconfont
color var(--accent-color)
.sidebar-group &
padding-left 1.75rem
.sidebar-sub-headers &
padding-top 0.25rem
padding-bottom 0.25rem
border-left none
&.active
background-color transparent
font-weight 500
</style>
import Vue from "vue";
import type { PageComputed } from "@mr-hope/vuepress-types";
import type { SidebarItem } from "@theme/utils/sidebar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
openGroupIndex: number;
}, {
refreshIndex(): void;
toggleGroup(index: number): void;
isActive(page: PageComputed): boolean;
}, unknown, {
items: SidebarItem[];
depth: number;
}>;
export default _default;
import Vue from "vue";
import SidebarGroup from "@theme/components/Sidebar/SidebarGroup.vue";
import SidebarLink from "@theme/components/Sidebar/SidebarLink.vue";
import { isActive } from "@theme/utils/path";
const descendantIsActive = (route, item) => {
if (item.type === "group")
return item.children.some((child) => {
if (child.type === "group")
return descendantIsActive(route, child);
return child.type === "page" && isActive(route, child.path);
});
return false;
};
const resolveOpenGroupIndex = (route, items) => {
for (let i = 0; i < items.length; i++)
if (descendantIsActive(route, items[i]))
return i;
return -1;
};
export default Vue.extend({
name: "SidebarLinks",
components: { SidebarGroup, SidebarLink },
props: {
items: {
type: Array,
required: true,
},
depth: { type: Number, required: true },
},
data: () => ({
openGroupIndex: 0,
}),
watch: {
$route() {
this.refreshIndex();
},
},
created() {
this.refreshIndex();
},
methods: {
refreshIndex() {
const index = resolveOpenGroupIndex(this.$route, this.items);
if (index > -1)
this.openGroupIndex = index;
},
toggleGroup(index) {
this.openGroupIndex = index === this.openGroupIndex ? -1 : index;
},
isActive(page) {
return isActive(this.$route, page.regularPath);
},
},
});
//# sourceMappingURL=SidebarLinks.js.map
\ No newline at end of file
{"version":3,"file":"SidebarLinks.js","sourceRoot":"","sources":["SidebarLinks.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,YAAY,MAAM,4CAA4C,CAAC;AACtE,OAAO,WAAW,MAAM,2CAA2C,CAAC;AACpE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAO7C,MAAM,kBAAkB,GAAG,CAAC,KAAY,EAAE,IAAiB,EAAW,EAAE;IACtE,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;QACvB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAsC,EAAE,EAAE;YACnE,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;gBAAE,OAAO,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YAEpE,OAAO,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;IAEL,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAAC,KAAY,EAAE,KAAoB,EAAU,EAAE;IAC3E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE;QACnC,IAAI,kBAAkB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;IAEpD,OAAO,CAAC,CAAC,CAAC;AACZ,CAAC,CAAC;AAEF,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,cAAc;IAEpB,UAAU,EAAE,EAAE,YAAY,EAAE,WAAW,EAAE;IAEzC,KAAK,EAAE;QACL,KAAK,EAAE;YACL,IAAI,EAAE,KAAgC;YACtC,QAAQ,EAAE,IAAI;SACf;QACD,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;KACxC;IAED,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,cAAc,EAAE,CAAC;KAClB,CAAC;IAEF,KAAK,EAAE;QACL,MAAM;YACJ,IAAI,CAAC,YAAY,EAAE,CAAC;QACtB,CAAC;KACF;IAED,OAAO;QACL,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED,OAAO,EAAE;QACP,YAAY;YACV,MAAM,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;YAE7D,IAAI,KAAK,GAAG,CAAC,CAAC;gBAAE,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;QAC9C,CAAC;QAED,WAAW,CAAC,KAAa;YACvB,IAAI,CAAC,cAAc,GAAG,KAAK,KAAK,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QACnE,CAAC;QAED,QAAQ,CAAC,IAAkB;YACzB,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QACjD,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<ul v-if="items.length" class="sidebar-links">
<li v-for="(item, index) in items" :key="index">
<SidebarGroup
v-if="item.type === 'group'"
:item="item"
:open="index === openGroupIndex"
:depth="depth"
@toggle="toggleGroup(index)"
/>
<SidebarLink v-else :item="item" />
</li>
</ul>
</template>
<script src="./SidebarLinks" />
import Vue from "vue";
import type { NavBarConfigItem as ResovledNavbarConfigItem } from "@theme/utils/navbar";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
navLinks: ResovledNavbarConfigItem[];
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import RepoLink from "@theme/components/Navbar/RepoLink.vue";
import SidebarDropdownLink from "@theme/components/Sidebar/SidebarDropdownLink.vue";
import NavLink from "@theme/components/Navbar/NavLink.vue";
import { getNavLinkItem } from "@theme/utils/navbar";
export default Vue.extend({
name: "SidebarNavLinks",
components: {
RepoLink,
SidebarDropdownLink,
NavLink,
},
computed: {
navLinks() {
const navbar = this.$themeLocaleConfig.nav || this.$themeConfig.nav || [];
return navbar.map((link) => getNavLinkItem(link));
},
},
});
//# sourceMappingURL=SidebarNavLinks.js.map
\ No newline at end of file
{"version":3,"file":"SidebarNavLinks.js","sourceRoot":"","sources":["SidebarNavLinks.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,QAAQ,MAAM,uCAAuC,CAAC;AAC7D,OAAO,mBAAmB,MAAM,mDAAmD,CAAC;AACpF,OAAO,OAAO,MAAM,sCAAsC,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAKrD,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,iBAAiB;IAEvB,UAAU,EAAE;QACV,QAAQ;QACR,mBAAmB;QACnB,OAAO;KACR;IAED,QAAQ,EAAE;QACR,QAAQ;YACN,MAAM,MAAM,GACV,IAAI,CAAC,kBAAkB,CAAC,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,IAAI,EAAE,CAAC;YAE7D,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<nav class="sidebar-nav-links">
<!-- user links -->
<div v-for="item in navLinks" :key="item.link" class="nav-item">
<SidebarDropdownLink v-if="item.type === 'links'" :item="item" />
<NavLink v-else :item="item" />
</div>
<RepoLink />
</nav>
</template>
<script src="./SidebarNavLinks" />
<style lang="stylus">
.sidebar-nav-links
display none
padding 0.5rem 0 0.75rem 0
border-bottom 1px solid var(--border-color)
@media (max-width $MQMedium)
display block
ul
padding 0
margin 0.25rem 0 0
list-style-type none
.nav-item
position relative
display block
padding 0.5rem 0 0.5rem 1.5rem
font-size 1.1em
line-height 1.25rem
&:first-child
margin-left 0
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
darkmode: "auto" | "off" | "on";
}, {
setDarkmode(status: "on" | "off" | "auto"): void;
toggleDarkmode(isDarkmode: boolean): void;
}, {
darkmodeConfig: "auto" | "auto-switch" | "switch" | "disable";
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import AutoIcon from "@theme/icons/AutoIcon.vue";
import DarkIcon from "@theme/icons/DarkIcon.vue";
import LightIcon from "@theme/icons/LightIcon.vue";
import { changeClass } from "@theme/utils/dom";
export default Vue.extend({
name: "DarkmodeSwitch",
components: { AutoIcon, DarkIcon, LightIcon },
data: () => ({
darkmode: "auto",
}),
computed: {
darkmodeConfig() {
return this.$themeConfig.darkmode || "auto-switch";
},
},
mounted() {
this.darkmode =
localStorage.getItem("darkmode") ||
"auto";
if (this.darkmodeConfig === "auto-switch")
if (this.darkmode === "auto")
this.setDarkmode("auto");
else
this.setDarkmode(this.darkmode);
else if (this.darkmodeConfig === "auto")
this.setDarkmode("auto");
else if (this.darkmodeConfig === "switch")
this.setDarkmode(this.darkmode);
// disabled
else
this.setDarkmode("off");
},
methods: {
setDarkmode(status) {
if (status === "on")
this.toggleDarkmode(true);
else if (status === "off")
this.toggleDarkmode(false);
else {
const isDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;
const isLightMode = window.matchMedia("(prefers-color-scheme: light)").matches;
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (event) => {
if (event.matches)
this.toggleDarkmode(true);
});
window
.matchMedia("(prefers-color-scheme: light)")
.addEventListener("change", (event) => {
if (event.matches)
this.toggleDarkmode(false);
});
if (isDarkMode)
this.toggleDarkmode(true);
else if (isLightMode)
this.toggleDarkmode(false);
else {
const timeHour = new Date().getHours();
this.toggleDarkmode(timeHour < 6 || timeHour >= 18);
}
}
this.darkmode = status;
localStorage.setItem("darkmode", status);
},
toggleDarkmode(isDarkmode) {
const classes = document.body.classList;
if (isDarkmode)
changeClass(classes, ["theme-dark"], ["theme-light"]);
else
changeClass(classes, ["theme-light"], ["theme-dark"]);
},
},
});
//# sourceMappingURL=DarkmodeSwitch.js.map
\ No newline at end of file
{"version":3,"file":"DarkmodeSwitch.js","sourceRoot":"","sources":["DarkmodeSwitch.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
<template>
<div class="darkmode-switch">
<template v-if="darkmodeConfig === 'auto-switch'">
<div
class="item day"
:class="{ active: darkmode === 'off' }"
@click="setDarkmode('off')"
>
<LightIcon />
</div>
<div
class="item auto"
:class="{ active: darkmode === 'auto' }"
@click="setDarkmode('auto')"
>
<AutoIcon />
</div>
<div
class="item night"
:class="{ active: darkmode === 'on' }"
@click="setDarkmode('on')"
>
<DarkIcon />
</div>
</template>
<div v-else-if="darkmodeConfig === 'switch'" class="switch">
<input
id="switch"
class="switch-input"
type="checkbox"
:checked="darkmode !== 'on'"
@click="setDarkmode(darkmode === 'on' ? 'off' : 'on')"
/>
<label class="label" for="switch">
<span class="label-content" />
</label>
</div>
</div>
</template>
<script src="./DarkmodeSwitch" />
<style lang="stylus">
@keyframes starry_star
50%
background rgba(255, 255, 255, 0.1)
box-shadow #fff 7.5px -0.75px 0 0, #fff 3px 2.5px 0 -0.25px, rgba(255, 255, 255, 0.1) 9.5px 4.5px 0 0.25px, #fff 8px 8.5px 0 0, rgba(255, 255, 255, 0.1) 5px 6px 0 -0.375px, #fff 1.25px 9.5px 0 0.25px
@keyframes bounceIn
0%
opacity 0
transform scale(0.3)
50%
opacity 100
transform scale(1.1)
55%
transform scale(1.1)
75%
transform scale(0.9)
100%
opacity 100
transform scale(1)
.darkmode-switch
display flex
height 22px
&:hover
cursor pointer
.item
padding 2px
border 1px solid var(--accent-color)
border-left none
line-height 1
&:first-child
border-left 1px solid var(--accent-color)
&.day
border-radius 4px 0 0 4px
&.night
border-radius 0 4px 4px 0
.icon
width 16px
height 16px
color var(--accent-color)
&.active
background var(--accent-color)
&:hover
cursor default
.icon
color var(--white)
.switch
display block
text-align center
user-select none
.label
display block
position relative
width 31.25px
height 17.5px
margin 0 auto
border-radius 17.5px
border 1px solid #1c1c1c
background #3c4145
font-size 1.4em
transition all 250ms ease-in
&:hover
cursor pointer
&:before
content ''
display block
position absolute
top 0.5px
left 1px
width 14px
height 14px
border 1.25px solid #e3e3c7
border-radius 50%
background #fff
transition all 250ms ease-in
&:after
content ''
display block
position absolute
top 62%
left 9.75px
z-index 10
width 2.8px
height 2.8px
opacity 0
background #fff
border-radius 50%
box-shadow #fff 0 0, #fff 0.75px 0, #fff 1.5px 0, #fff 2.25px 0, #fff 2.75px 0, #fff 3.5px 0, #fff 4px 0, #fff 5.25px -0.25px 0 0.25px, #fff 4px -1.75px 0 -0.5px, #fff 1.75px -1.75px 0 0.25px, #d3d3d3 0 0 0 1px, #d3d3d3 1.5px 0 0 1px, #d3d3d3 2.75px 0 0 1px, #d3d3d3 4px 0 0 1px, #d3d3d3 5.25px -0.25px 0 1.25px, #d3d3d3 4px -1.75px 0 0.25px, #d3d3d3 1.75px -1.75px 0 1.25px
transition opacity 100ms ease-in
.label-content
display block
position absolute
top 2.25px
left 52.5%
z-index 20
width 1px
height 1px
border-radius 50%
background #fff
box-shadow rgba(255, 255, 255, 0.1) 7.5px -0.75px 0 0, rgba(255, 255, 255, 0.1) 3px 2.5px 0 -0.25px, #fff 9.5px 4.5px 0 0.25px, rgba(255, 255, 255, 0.1) 8px 8.5px 0 0, #fff 5px 6px 0 0.375px, rgba(255, 255, 255, 0.1) 1.25px 9.5px 0 0.25px
animation starry_star 5s ease-in-out infinite
transition all 250ms ease-in
&:before
content ''
display block
position absolute
top -0.5px
left -6.25px
width 4.5px
height 4.5px
background #fff
border-radius 50%
border 1.25px solid #e3e3c7
box-shadow #e3e3c7 -7px 0 0 -0.75px, #e3e3c7 -2px 6px 0 -0.5px
transform-origin -1.5px 130%
transition all 250ms ease-in
.switch-input
display none
transition all 250ms ease-in
&:checked + .label
background #9ee3fb
border 1px solid #86c3d7
&:before
left 13.75px
background #ffdf6d
border 1.25px solid #e1c348
&:after
opacity 100
animation bounceIn 0.6s ease-in-out 0.1s
animation-fill-mode backwards
& > .label-content
opacity 0
box-shadow rgba(255, 255, 255, 0.1) 7.5px -0.75px 0 -1px, rgba(255, 255, 255, 0.1) 3px 2.5px 0 -1.25px, #fff 9.5px 4.5px 0 -0.75px, rgba(255, 255, 255, 0.1) 8px 8.5px 0 -1px, #fff 5px 6px 0 -1.375px, rgba(255, 255, 255, 0.1) 1.25px 9.5px 0 -0.75px
animation none
&:before
left 6.25px
transform rotate(70deg)
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
showMenu: boolean;
}, {
clickOutside(): void;
}, unknown, Record<never, any>>;
export default _default;
import Vue from "vue";
import ClickOutside from "@theme/utils/click-outside";
import ThemeOptions from "@theme/components/Theme/ThemeOptions.vue";
export default Vue.extend({
name: "ThemeColor",
directives: { "click-outside": ClickOutside },
components: { ThemeOptions },
data: () => ({
showMenu: false,
}),
methods: {
clickOutside() {
this.showMenu = false;
},
},
});
//# sourceMappingURL=ThemeColor.js.map
\ No newline at end of file
{"version":3,"file":"ThemeColor.js","sourceRoot":"","sources":["ThemeColor.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,YAAY,MAAM,0CAA0C,CAAC;AAEpE,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,YAAY;IAElB,UAAU,EAAE,EAAE,eAAe,EAAE,YAAY,EAAE;IAE7C,UAAU,EAAE,EAAE,YAAY,EAAE;IAE5B,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,QAAQ,EAAE,KAAK;KAChB,CAAC;IAEF,OAAO,EAAE;QACP,YAAY;YACV,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACxB,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<button
v-click-outside="clickOutside"
class="color-button"
:class="{ select: showMenu }"
tabindex="-1"
aria-hidden="true"
@click="showMenu = !showMenu"
>
<svg
class="skin-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M224 800c0 9.6 3.2 44.8 6.4 54.4 6.4 48-48 76.8-48 76.8s80 41.6 147.2 0 134.4-134.4
38.4-195.2c-22.4-12.8-41.6-19.2-57.6-19.2C259.2 716.8 227.2 761.6 224 800zM560 675.2l-32
51.2c-51.2 51.2-83.2 32-83.2 32 25.6 67.2 0 112-12.8 128 25.6 6.4 51.2 9.6 80 9.6 54.4 0
102.4-9.6 150.4-32l0 0c3.2 0 3.2-3.2 3.2-3.2 22.4-16 12.8-35.2
6.4-44.8-9.6-12.8-12.8-25.6-12.8-41.6 0-54.4 60.8-99.2 137.6-99.2 6.4 0 12.8 0 22.4
0 12.8 0 38.4 9.6 48-25.6 0-3.2 0-3.2 3.2-6.4 0-3.2 3.2-6.4 3.2-6.4 6.4-16 6.4-16 6.4-19.2
9.6-35.2 16-73.6 16-115.2 0-105.6-41.6-198.4-108.8-268.8C704 396.8 560 675.2 560 675.2zM224
419.2c0-28.8 22.4-51.2 51.2-51.2 28.8 0 51.2 22.4 51.2 51.2 0 28.8-22.4 51.2-51.2 51.2C246.4
470.4 224 448 224 419.2zM320 284.8c0-22.4 19.2-41.6 41.6-41.6 22.4 0 41.6 19.2 41.6 41.6 0
22.4-19.2 41.6-41.6 41.6C339.2 326.4 320 307.2 320 284.8zM457.6 208c0-12.8 12.8-25.6 25.6-25.6
12.8 0 25.6 12.8 25.6 25.6 0 12.8-12.8 25.6-25.6 25.6C470.4 233.6 457.6 220.8 457.6 208zM128
505.6C128 592 153.6 672 201.6 736c28.8-60.8 112-60.8 124.8-60.8-16-51.2 16-99.2
16-99.2l316.8-422.4c-48-19.2-99.2-32-150.4-32C297.6 118.4 128 291.2 128 505.6zM764.8
86.4c-22.4 19.2-390.4 518.4-390.4 518.4-22.4 28.8-12.8 76.8 22.4 99.2l9.6 6.4c35.2 22.4
80 12.8 99.2-25.6 0 0 6.4-12.8 9.6-19.2 54.4-105.6 275.2-524.8 288-553.6
6.4-19.2-3.2-32-19.2-32C777.6 76.8 771.2 80 764.8 86.4z"
/>
</svg>
<transition mode="out-in" name="menu-transition">
<div v-show="showMenu" class="color-picker-menu">
<ThemeOptions />
</div>
</transition>
</button>
</template>
<script src="./ThemeColor" />
<style lang="stylus">
@require '~@mr-hope/vuepress-shared/styles/reset'
.color-button
button()
position relative
width 2.25rem
height 2.25rem
margin 0 0.25rem
padding 0.5rem
outline none
color #aaa
flex-shrink 0
&:hover, &.select
color var(--accent-color)
&.select:hover
color #aaa
.skin-icon
width 100%
height 100%
fill currentcolor
.color-picker-menu
position absolute
top: $navbarHeight - $navbarVerticalPadding
left 50%
min-width 100px
margin 0
padding 0.5em 0.75em
background var(--bgcolor)
box-shadow 2px 2px 10px var(--card-shadow-color)
color var(--dark-grey)
border-radius 0.25em
transform translateX(-50%)
z-index 250
@media (max-width $MQMobile)
top: $navbarMobileHeight - $navbarMobileVerticalPadding
transform translateX(-80%)
&::before
content ''
position absolute
top -7px
left 50%
border-style solid
border-color transparent transparent var(--bgcolor)
border-width 0 7px 7px
transform translateX(-50%)
@media (max-width $MQMobile)
left 80%
&.menu-transition-enter-active, &.menu-transition-leave-active
transition all 0.25s ease-in-out
&.menu-transition-enter, &.menu-transition-leave-to
top 30px
opacity 0
ul
list-style-type none
margin 0
padding 0
@media (max-width $MQMobile)
.color-picker
.color-picker-menu
left calc(50% - 35px)
&::before
left calc(50% + 35px)
</style>
import Vue from "vue";
interface ThemeColor {
/** Color list */
list: string[];
/** Color picker */
picker: Record<string, string>;
}
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
themeColor: ThemeColor;
isDarkmode: boolean;
}, {
setTheme(theme?: string | undefined): void;
}, {
text: {
themeColor: string;
themeMode: string;
};
themeColorEnabled: boolean;
switchEnabled: boolean;
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import { getDefaultLocale } from "@mr-hope/vuepress-shared";
import DarkmodeSwitch from "@theme/components/Theme/DarkmodeSwitch.vue";
const defaultColorPicker = {
red: "#e74c3c",
blue: "#3498db",
green: "#3eaf7c",
orange: "#f39c12",
purple: "#8e44ad",
};
export default Vue.extend({
name: "ThemeOptions",
components: { DarkmodeSwitch },
data: () => ({
themeColor: {},
isDarkmode: false,
}),
computed: {
text() {
return (this.$themeLocaleConfig.themeColor || getDefaultLocale().themeColor);
},
themeColorEnabled() {
return this.$themeConfig.themeColor !== false;
},
switchEnabled() {
return (this.$themeConfig.darkmode !== "disable" &&
this.$themeConfig.darkmode !== "auto");
},
},
mounted() {
const theme = localStorage.getItem("theme");
this.themeColor = {
list: this.$themeConfig.themeColor
? Object.keys(this.$themeConfig.themeColor)
: Object.keys(defaultColorPicker),
picker: this.$themeConfig.themeColor || defaultColorPicker,
};
if (theme)
this.setTheme(theme);
},
methods: {
setTheme(theme) {
const classes = document.body.classList;
const themes = this.themeColor.list.map((colorTheme) => `theme-${colorTheme}`);
if (!theme) {
localStorage.removeItem("theme");
classes.remove(...themes);
return;
}
classes.remove(...themes.filter((themeclass) => themeclass !== `theme-${theme}`));
classes.add(`theme-${theme}`);
localStorage.setItem("theme", theme);
},
},
});
//# sourceMappingURL=ThemeOptions.js.map
\ No newline at end of file
{"version":3,"file":"ThemeOptions.js","sourceRoot":"","sources":["ThemeOptions.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,cAAc,MAAM,4CAA4C,CAAC;AAIxE,MAAM,kBAAkB,GAA2B;IACjD,GAAG,EAAE,SAAS;IACd,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;CAClB,CAAC;AASF,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,cAAc;IAEpB,UAAU,EAAE,EAAE,cAAc,EAAE;IAE9B,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,UAAU,EAAE,EAAgB;QAE5B,UAAU,EAAE,KAAK;KAClB,CAAC;IAEF,QAAQ,EAAE;QACR,IAAI;YACF,OAAO,CACL,IAAI,CAAC,kBAAkB,CAAC,UAAU,IAAI,gBAAgB,EAAE,CAAC,UAAU,CACpE,CAAC;QACJ,CAAC;QAED,iBAAiB;YACf,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,KAAK,KAAK,CAAC;QAChD,CAAC;QAED,aAAa;YACX,OAAO,CACL,IAAI,CAAC,YAAY,CAAC,QAAQ,KAAK,SAAS;gBACxC,IAAI,CAAC,YAAY,CAAC,QAAQ,KAAK,MAAM,CACtC,CAAC;QACJ,CAAC;KACF;IAED,OAAO;QACL,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAE5C,IAAI,CAAC,UAAU,GAAG;YAChB,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,UAAU;gBAChC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC;gBAC3C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC;YACnC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC,UAAU,IAAI,kBAAkB;SAC3D,CAAC;QAEF,IAAI,KAAK;YAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED,OAAO,EAAE;QACP,QAAQ,CAAC,KAAc;YACrB,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;YACxC,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CACrC,CAAC,UAAU,EAAE,EAAE,CAAC,SAAS,UAAU,EAAE,CACtC,CAAC;YAEF,IAAI,CAAC,KAAK,EAAE;gBACV,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;gBACjC,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;gBAE1B,OAAO;aACR;YAED,OAAO,CAAC,MAAM,CACZ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,KAAK,SAAS,KAAK,EAAE,CAAC,CAClE,CAAC;YAEF,OAAO,CAAC,GAAG,CAAC,SAAS,KAAK,EAAE,CAAC,CAAC;YAC9B,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="theme-options">
<ul v-if="themeColorEnabled" class="themecolor-select">
<label for="themecolor-select" v-text="`${text.themeColor}:`" />
<li>
<span class="default-theme" @click="setTheme()" />
</li>
<li v-for="color in themeColor.list" :key="color">
<span
:style="{ background: themeColor.picker[color] }"
@click="setTheme(color)"
/>
</li>
</ul>
<div v-if="switchEnabled" class="darkmode-toggle">
<label class="desc" for="darkmode-toggle" v-text="`${text.themeMode}:`" />
<DarkmodeSwitch />
<ScreenFull />
</div>
</div>
</template>
<script src="./ThemeOptions" />
<style lang="stylus">
.theme-options
font-size 14px
.themecolor-select
display flex
justify-content space-around
label
padding-right 8px
li
&:first-child
margin-right 8px
span
vertical-align middle
width 15px
height 15px
margin 0 2px
border-radius 2px
&.default-theme
background $accentColor // must be fixed to the original accent-color
.darkmode-toggle
display flex
align-items center
margin-top 8px
.desc
padding-right 8px
line-height 1.5
.full-screen, .cancel-full-screen
margin-left 0.5em
</style>
<template>
<svg class="icon article-icon" viewBox="0 0 1024 1024">
<path
d="M853.333 938.667H170.667A42.667 42.667 0 0 1 128 896V128a42.667 42.667 0 0 1 42.667-42.667h682.666A42.667 42.667 0 0 1 896 128v768a42.667 42.667 0 0 1-42.667 42.667zm-42.666-85.334V170.667H213.333v682.666h597.334zM298.667 256h170.666v170.667H298.667V256zm0 256h426.666v85.333H298.667V512zm0 170.667h426.666V768H298.667v-85.333zm256-384h170.666V384H554.667v-85.333z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon auto-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M460.864 539.072H564.8L510.592 376l-49.728 163.072zM872 362.368V149.504H659.648L510.528 0l-149.12 149.504H149.12v212.928L0 511.872l149.12 149.504v212.928h212.352l149.12 149.504 149.12-149.504h212.352V661.376l149.12-149.504L872 362.368zM614.464 693.12l-31.616-90.624H438.272l-31.616 90.624h-85.888l144.576-407.68h90.368l144.576 407.68h-85.824zm0 0"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon book-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M256 853.333h426.667A85.333 85.333 0 0 0 768 768V256a85.333 85.333 0 0 0-85.333-85.333H469.333a42.667 42.667 0 0 1 0-85.334h213.334A170.667 170.667 0 0 1 853.333 256v512a170.667 170.667 0 0 1-170.666 170.667H213.333A42.667 42.667 0 0 1 170.667 896V128a42.667 42.667 0 0 1 42.666-42.667h128A42.667 42.667 0 0 1 384 128v304.256l61.653-41.088a42.667 42.667 0 0 1 47.36 0l61.654 41.045V256A42.667 42.667 0 0 1 640 256v256a42.667 42.667 0 0 1-66.347 35.499l-104.32-69.547-104.32 69.547A42.667 42.667 0 0 1 298.667 512V170.667H256v682.666z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon dark-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M935.539 630.402c-11.43-11.432-28.674-14.739-43.531-8.354-46.734 20.103-96.363 30.297-147.508 30.297-99.59 0-193.221-38.784-263.64-109.203-108.637-108.637-139.61-270.022-78.908-411.148a39.497 39.497 0 0 0-51.886-51.887c-52.637 22.64-100.017 54.81-140.826 95.616-85.346 85.346-132.346 198.821-132.346 319.52 0 120.7 47.001 234.172 132.347 319.519S408.063 947.11 528.76 947.11c120.7 0 234.172-47.003 319.52-132.351 40.809-40.81 72.978-88.19 95.616-140.826a39.497 39.497 0 0 0-8.356-43.532z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon edit-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M117.953 696.992 64.306 959.696l265.931-49.336 450.204-452.505-212.284-213.376-450.204 452.513zm496.384-296.326L219.039 797.993l-46.108-46.34L568.233 354.33l46.104 46.335zm345.357-122.99-114.45 115.04-212.288-213.377 114.45-115.035 212.288 213.371zm0 0"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="empty-icon"
viewBox="0 0 1024 1024"
>
<defs>
<linearGradient
id="linear-gradient"
x1="512.342"
y1="2266.13"
x2="512.342"
y2="666.063"
gradientUnits="userSpaceOnUse"
>
<stop offset=".919" stop-color="#e6e6e6" stop-opacity="0" />
<stop offset="1" stop-color="#e6e6e6" />
</linearGradient>
<linearGradient
id="linear-gradient-2"
x1="528.912"
y1="774"
x2="388.088"
y2="612"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#ccc" />
<stop offset="1" stop-color="#e6e6e6" stop-opacity="0" />
</linearGradient>
<linearGradient
id="linear-gradient-3"
x1="213.219"
y1="721.704"
x2="251.313"
y2="683.61"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#d7d7d7" />
<stop offset=".485" stop-color="#fafafa" />
<stop offset="1" stop-color="#fafafa" />
</linearGradient>
<linearGradient
id="linear-gradient-4"
x1="724.813"
y1="821.718"
x2="768.656"
y2="777.876"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#ccc" />
<stop offset="1" stop-color="#fafafa" />
</linearGradient>
<linearGradient
id="linear-gradient-5"
x1="513.493"
y1="714.594"
x2="471.007"
y2="544.188"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#999" />
<stop offset="1" stop-color="#ccc" />
</linearGradient>
<linearGradient
id="linear-gradient-6"
x1="440.156"
y1="564.031"
x2="508.594"
y2="495.594"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#ccc" />
<stop offset="1" stop-color="#f0f0f0" />
</linearGradient>
<linearGradient
id="linear-gradient-7"
x1="660.988"
y1="754.156"
x2="608.637"
y2="544.188"
xlink:href="#linear-gradient-5"
/>
<linearGradient
id="linear-gradient-8"
x1="479.188"
y1="774.219"
x2="649.782"
y2="603.625"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#b3b3b3" />
<stop offset="1" stop-color="#e6e6e6" />
</linearGradient>
<linearGradient
id="linear-gradient-9"
x1="447.121"
y1="774.219"
x2="394.661"
y2="563.813"
xlink:href="#linear-gradient-5"
/>
<linearGradient
id="linear-gradient-10"
x1="494"
y1="597"
x2="628"
y2="463"
xlink:href="#linear-gradient-6"
/>
<linearGradient
id="linear-gradient-11"
x1="610.485"
y1="604.938"
x2="697.298"
y2="518.125"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#ccc" />
<stop offset="1" stop-color="#fff" />
</linearGradient>
<linearGradient
id="linear-gradient-12"
x1="457.438"
y1="619.25"
x2="353.469"
y2="619.25"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#e6e6e6" stop-opacity="0" />
<stop offset="1" stop-color="#e6e6e6" />
</linearGradient>
<linearGradient
id="linear-gradient-14"
x1="542.734"
y1="674.25"
x2="615.672"
y2="601.313"
xlink:href="#linear-gradient-6"
/>
<linearGradient
id="linear-gradient-15"
x1="627.933"
y1="358.938"
x2="685.192"
y2="422.531"
gradientUnits="userSpaceOnUse"
>
<stop offset=".4" stop-color="#e6e6e6" stop-opacity=".4" />
<stop offset=".443" stop-color="#fff" />
<stop offset=".6" stop-color="#ccc" />
</linearGradient>
<linearGradient
id="linear-gradient-16"
x1="618.547"
y1="422.531"
x2="681.547"
y2="359.531"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#e6e6e6" />
<stop offset=".761" stop-color="#fff" />
<stop offset="1" stop-color="#f0f0f0" />
</linearGradient>
<linearGradient
id="linear-gradient-17"
x1="625"
y1="441.5"
x2="697"
y2="369.5"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#ccc" />
<stop offset=".761" stop-color="#fff" />
<stop offset="1" stop-color="#f0f0f0" />
</linearGradient>
<linearGradient
id="linear-gradient-18"
x1="627.681"
y1="361.438"
x2="692.257"
y2="433.156"
xlink:href="#linear-gradient-15"
/>
<linearGradient
id="linear-gradient-19"
x1="561.414"
y1="735.438"
x2="573.149"
y2="688.375"
xlink:href="#linear-gradient-11"
/>
<linearGradient
id="linear-gradient-20"
x1="405"
y1="485.875"
x2="440"
y2="450.875"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#ccc" />
<stop offset="1" stop-color="#fff" stop-opacity=".702" />
</linearGradient>
<linearGradient
id="linear-gradient-21"
x1="404.61"
y1="486.906"
x2="441.86"
y2="449.656"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#ccc" />
<stop offset=".495" stop-color="#ccc" stop-opacity=".702" />
<stop offset=".498" stop-color="#ccc" />
<stop offset="1" stop-color="#fff" stop-opacity=".302" />
</linearGradient>
<radialGradient
id="radial-gradient"
cx="329.297"
cy="647.578"
r="8.172"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#fafafa" />
<stop offset="1.2" stop-color="#e6e6e6" />
</radialGradient>
<radialGradient
id="radial-gradient-2"
cx="802.297"
cy="673.578"
r="8.172"
xlink:href="#radial-gradient"
/>
<radialGradient
id="radial-gradient-3"
cx="774.844"
cy="642.75"
r="5.531"
xlink:href="#radial-gradient"
/>
<style>
.cls-17,
.cls-19,
.cls-27,
.cls-28,
.cls-29,
.cls-3,
.cls-30,
.cls-6 {
fill-rule: evenodd;
}
.cls-3 {
fill: #b3b3b3;
}
.cls-6 {
fill: #ccc;
}
.cls-17 {
fill: url(#linear-gradient-12);
}
.cls-19 {
fill: #fff;
}
.cls-27 {
fill: #f8cfad;
}
.cls-28 {
fill: #141a33;
}
.cls-29 {
fill: #f0c5a8;
}
.cls-30 {
fill: #232c57;
}
</style>
</defs>
<g>
<path
d="M512.33 666.07c441.828 0 800 358.18 800 800.03s-358.172 800.02-800 800.02-800-358.18-800-800.02 358.17-800.03 800-800.03z"
style="fill: url(#linear-gradient); fill-rule: evenodd"
/>
<path
d="m272 694 242-82 131 119-188 43z"
style="fill: url(#linear-gradient-2); fill-rule: evenodd"
/>
<path
class="cls-3"
d="M232.391 723.534a2.4 2.4 0 0 1 2.4 2.4v17.725a2.4 2.4 0 0 1-4.8 0v-17.725a2.4 2.4 0 0 1 2.4-2.4z"
/>
<path
d="M232.255 676.559c10.33 0 17.067 15.408 18.7 28.493 1.619 12.942-2.372 23.694-18.7 23.694-16.878 0-20.213-10.733-18.7-23.694 1.633-14.061 8.37-28.493 18.7-28.493z"
style="fill: url(#linear-gradient-3); fill-rule: evenodd"
/>
<path
class="cls-3"
d="M745.853 826h.938a2.4 2.4 0 0 1 2.4 2.4v22.238a2.4 2.4 0 0 1-2.4 2.4h-.938a2.4 2.4 0 0 1-2.4-2.4V828.4a2.4 2.4 0 0 1 2.4-2.4z"
/>
<path
d="M746.727 830.3c-19.438 0-23.278-9.326-21.541-20.59a34.467 34.467 0 0 1 3.289-10.369 16.628 16.628 0 0 1 0-9.112c2.889-12.327 12.059-20.911 18.356-20.911 6.56 0 15.468 9.1 18.356 20.911a14.589 14.589 0 0 1-.335 9.217 34.36 34.36 0 0 1 3.419 10.264c1.861 11.243-2.735 20.59-21.544 20.59z"
style="fill: url(#linear-gradient-4); fill-rule: evenodd"
/>
<path
class="cls-6"
d="M328.841 654.562a6.571 6.571 0 0 0-5.2-5.027q-4.107-.952-.034-2.045a6.571 6.571 0 0 0 5.027-5.2q.952-4.109 2.045-.035a6.569 6.569 0 0 0 5.2 5.027q4.109.954.035 2.045a6.569 6.569 0 0 0-5.027 5.2q-.955 4.108-2.046.035z"
/>
<path
d="M328.383 653.73a6.567 6.567 0 0 0-5.2-5.027q-4.109-.954-.035-2.045a6.568 6.568 0 0 0 5.027-5.2q.954-4.107 2.046-.034a6.568 6.568 0 0 0 5.2 5.027q4.107.952.035 2.045a6.568 6.568 0 0 0-5.027 5.2q-.954 4.104-2.046.034z"
style="fill: url(#radial-gradient); fill-rule: evenodd"
/>
<path
class="cls-6"
d="M801.841 680.562a6.571 6.571 0 0 0-5.2-5.027q-4.107-.952-.034-2.045a6.571 6.571 0 0 0 5.027-5.2q.952-4.109 2.045-.035a6.569 6.569 0 0 0 5.2 5.027q4.108.954.035 2.045a6.569 6.569 0 0 0-5.027 5.2q-.955 4.108-2.046.035z"
/>
<path
d="M801.383 679.73a6.567 6.567 0 0 0-5.2-5.027q-4.108-.954-.035-2.045a6.568 6.568 0 0 0 5.027-5.2q.954-4.107 2.046-.034a6.568 6.568 0 0 0 5.2 5.027q4.107.952.035 2.045a6.568 6.568 0 0 0-5.027 5.2q-.954 4.104-2.046.034z"
style="fill: url(#radial-gradient-2); fill-rule: evenodd"
/>
<path
d="M774.21 646.9a4.446 4.446 0 0 0-3.517-3.4q-2.778-.643-.023-1.383a4.443 4.443 0 0 0 3.4-3.517q.645-2.778 1.383-.023a4.443 4.443 0 0 0 3.517 3.4q2.778.645.023 1.383a4.446 4.446 0 0 0-3.4 3.517q-.645 2.78-1.383.023z"
style="fill: url(#radial-gradient-3); fill-rule: evenodd"
/>
<path
d="m385.6 714.6.158-150.658L598.9 544.174l-.158 150.658z"
style="fill: url(#linear-gradient-5); fill-rule: evenodd"
/>
<path
d="m385.474 564.031 214.763-19.383-36.171-49.067-215.559 17.634z"
style="fill: url(#linear-gradient-6); fill-rule: evenodd"
/>
<path
d="m598.744 694.832.156-150.658 71.975 59.319-.158 150.658z"
style="fill: url(#linear-gradient-7); fill-rule: evenodd"
/>
<path
d="m457.064 774.209.158-150.658 214.691-19.914-.158 150.663z"
style="fill: url(#linear-gradient-8); fill-rule: evenodd"
/>
<path
d="m384.566 714.459.158-150.659 72.5 59.75-.158 150.658z"
style="fill: url(#linear-gradient-9); fill-rule: evenodd"
/>
<path
d="M494 640s75.357-58.4 42-83-38.887 1.663-37 14 53.847 12.465 54-26c.2-49.979 75-125 75-125"
style="
fill: none;
stroke-width: 3px;
stroke-dasharray: 12 6;
stroke: url(#linear-gradient-10);
fill-rule: evenodd;
"
/>
<path
d="m670.275 604.939-72.041-59.9 38.476-26.909 72.86 58.159z"
style="fill: url(#linear-gradient-11); fill-rule: evenodd"
/>
<path
class="cls-17"
d="m425.5 674.383-72.042-59.9 31.109-50.347 72.86 58.16z"
/>
<path
class="cls-17"
d="m425.5 674.383-72.042-59.9 31.109-50.347 72.86 58.16z"
/>
<path
d="m487.918 674.235 214.482-22.57-31.1-50.346-215.309 20.833z"
style="fill: url(#linear-gradient-14); fill-rule: evenodd"
/>
<path class="cls-19" d="m697.363 358.927-69.58 62.511-12.035 1.082z" />
<path
d="m697.363 358.927-69.58 62.511-12.035 1.082z"
style="fill: url(#linear-gradient-15); fill-rule: evenodd"
/>
<path
d="M615.748 422.52 604 413l92.089-53.46"
style="fill: url(#linear-gradient-16); fill-rule: evenodd"
/>
<path
d="m625 432 12 18 60-89"
style="fill: url(#linear-gradient-17); fill-rule: evenodd"
/>
<path class="cls-19" d="m626.98 421.335-2.471 11.828 70.918-71.735" />
<path
d="m626.98 421.335-2.471 11.828 70.918-71.735"
style="fill: url(#linear-gradient-18); fill-rule: evenodd"
/>
<path
d="m494.814 735.44 21.293-2.1v-6.613l-13.4 1.319v-6.965l10.977-1.08v-6.613l-10.977 1.08v-6.084l12.917-1.27v-6.525l-20.808 2.047v32.8zM521 732.863l7.054-.694v-11.241a106.361 106.361 0 0 0-1.014-11.274l.176-.017 2.645 7.586 4.453 11.553 4.32-.425 4.408-12.424 2.733-8.116.177-.018a111.811 111.811 0 0 0-1.014 11.474v11.241l7.185-.707V697l-8.552.841-5.025 14.646c-.618 1.956-1.147 4.08-1.808 6.173l-.22.022c-.617-1.968-1.146-3.987-1.808-5.818l-5.2-13.639-8.508.837v32.8zm37.213-3.661 7.891-.776v-10.889l3.835-.377c6.922-.681 12.961-4.714 12.961-12.517 0-8.111-5.951-10.082-13.181-9.371l-11.504 1.128v32.8zm7.891-17.881v-9.478l3.218-.316c3.792-.373 5.908.565 5.908 3.871 0 3.218-1.852 5.208-5.687 5.585zM594 725.682l7.891-.777v-26.274l8.905-.876v-6.524l-25.657 2.524v6.524l8.861-.871v26.274zm27.991-2.754 7.847-.772v-11.594l9.919-22.18-8.244.811-2.733 7.542c-.925 2.56-1.807 4.939-2.733 7.587l-.176.018c-.926-2.466-1.764-4.676-2.645-7.058l-2.734-7-8.375.824 9.874 20.233v11.594z"
style="fill: url(#linear-gradient-19); fill-rule: evenodd"
/>
<path
class="cls-6"
d="M408.938 457.309a17.5 17.5 0 0 0 21.374 26.725 17.5 17.5 0 1 1-16.306-30.955 17.442 17.442 0 0 0-5.068 4.23z"
/>
<circle
cx="422.5"
cy="468.375"
r="17.5"
style="fill: url(#linear-gradient-20)"
/>
<path
class="cls-6"
d="M391.76 451.5c-2.358 4.419 9.827 15.52 27.215 24.8 15.131 8.071 29.212 12.1 34.166 10.093-4.191 2.772-18.943-1.24-34.86-9.73-17.388-9.275-29.573-20.376-27.215-24.8a2.96 2.96 0 0 1 1.585-1.3 2.606 2.606 0 0 0-.891.937z"
/>
<path
d="M418.975 476.29c-17.388-9.275-29.573-20.376-27.215-24.8s18.363-.484 35.751 8.791 29.572 20.376 27.215 24.8-18.364.483-35.751-8.791zm31.634 5.732c1.824-3.42-8.789-12.642-23.7-20.6s-28.486-11.633-30.31-8.213 8.789 12.642 23.7 20.6 28.486 11.633 30.31 8.213zm-36.645-29.008-2.775 1.452.032 1.751 28.637 14.183.266-4.559z"
style="fill: url(#linear-gradient-21); fill-rule: evenodd"
/>
</g>
<g class="people">
<path
class="cls-27"
d="m612.131 676.5 1.362 3.532 3.255-2.324-1.361-3.532z"
/>
<path
class="cls-27"
d="m629.131 665.5 1.362 3.532 3.255-2.324-1.361-3.532z"
/>
<path
class="cls-28"
d="m617.764 678.184-3.162-.078a11.028 11.028 0 0 0-1.034 3.454c-.258 2.006-1.177 5-.449 5.367 1.5 2.659 4.118-.215 4.118-.215s2.187-2.848 1.925-5.265c-.106-.973-1.181-1.869-1.398-3.263z"
/>
<path
class="cls-28"
d="m633.781 665.855 3.019.945a11.008 11.008 0 0 1-.137 3.6c-.4 1.981-.179 4.166-.986 4.277-2.283 2.03-3.827-1.533-3.827-1.533s-1.473-2.456-.444-4.659c.412-.88 1.718-1.385 2.375-2.63z"
/>
<path
class="cls-29"
d="M599.935 592.534s10.293 9.761 11.95 7.564 3.536-3.463-6.758-13.65z"
/>
<path
class="cls-27"
d="M611.3 596.361c1.674-1.105 11.5 7.048 14.5 11.774s-12.705-4.36-14.632-6.776-1.54-3.893.132-4.998z"
/>
<path
class="cls-27"
d="M621.815 607.988s1.809 2.549 2.433 1.756 2.475-1.064 2.449-1.138.1-.819 1.288-2.331-3.8-3.632-5.81-.494a2.556 2.556 0 0 0-.36 2.207z"
/>
<path
class="cls-30"
d="M598 617s14.968-5.618 17 7a150.235 150.235 0 0 1 2 22s12.666 11.836 16 19c0 0-4.753-1.629-4 2 0 0-18.132-14.647-19-19s-9.148-18.716-12-31z"
/>
<path
d="M589 622s14.968-5.618 17 7a150.235 150.235 0 0 1 2 22s4.666 17.836 8 25c0 0-4.753-1.629-4 2 0 0-10.132-20.647-11-25s-9.148-18.716-12-31z"
style="fill: #292966; fill-rule: evenodd"
/>
<path
class="cls-29"
d="M585.626 597.7s-10.292 9.761-11.95 7.563-3.536-3.463 6.758-13.65z"
/>
<path
class="cls-27"
d="M574.259 601.529c-1.675-1.105-11.5 7.049-14.5 11.774s12.7-4.36 14.631-6.775 1.543-3.894-.131-4.999z"
/>
<path
class="cls-29"
d="M591.715 577.752s-.606 1.681 1.48 3.716-3.615 5.307-4.645 2.85-.48-2.716-.48-2.716z"
/>
<path
class="cls-27"
d="M583.527 574.123c-.839 1.043.491 3.873 1.453 5.449s2.749 2.833 3.364 2.428 4.088-2.657 4-4-.228-3.4-.228-3.4 2.562-1.641 2.154-2.916-2.916-.154-2.916-.154a15.853 15.853 0 0 0-.227-2.224c-.189-.929-6.887-1.445-7.827 2.6s.558 1.805.227 2.217z"
/>
<path
class="cls-30"
d="M584.227 567.758c2.1-.885 7.2-3.684 10.125.318s.842 4.385.989 5.294-1.894 5.69-1.341 6.63-3.865.8-4.657-1.179-2.844-.539-2.227-1.224-1.3-4.456-2.916-2.154a9.252 9.252 0 0 0 .309-1.38c-.115.192.259-3.257-.673-1.32s-2.1 1.037-3.069.762-1.8-1.118-1.071-1.689c.023-.016 2.436-3.172 4.531-4.058z"
/>
<path
d="M589 585c-2.584-.47-10.055.362-13 13 0 0 1.9 3.349 5 4s6 21 6 21 24.016 11.06 27-3c-.07-13.826-8-21-8-21s5.829-3.2 5-6-8.016-10.153-11-10-6 0-6 0-2.416 2.47-5 2z"
style="fill: #f6bb07; fill-rule: evenodd"
/>
<path
class="cls-27"
d="M563.284 612.581s-.986 2.965-1.814 2.389-2.678-.3-2.675-.374-.333-.755-1.912-1.854 2.577-4.583 5.414-2.167a2.551 2.551 0 0 1 .987 2.006z"
/>
</g>
</svg>
</template>
<style lang="stylus">
.empty-icon
.theme-dark &
g.people
opacity 0.8
g:not(.people)
filter invert(80%)
</style>
<template>
<svg
class="icon i18n-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M639.981 344.075c14.805 44.45 34.542 79.023 69.084 113.596 29.603-29.634 49.34-69.146 64.145-113.596H639.981zM314.33 591.024h128.29l-64.145-172.865-64.145 172.865z"
fill="currentColor"
/>
<path
d="M807.746 116.882H215.643c-54.274 0-98.681 44.45-98.681 98.78v592.677c0 54.329 44.407 98.78 98.68 98.78h592.104c54.273 0 98.681-44.451 98.681-98.78V215.66c0-54.329-39.475-98.78-98.68-98.78zM565.971 754.01c-9.866 9.878-19.738 9.878-29.603 9.878-4.94 0-14.805 0-19.738-4.939-4.939-4.939-9.872 0-9.872-4.939s-4.932-9.878-9.865-19.756c-4.94-9.878-4.94-14.817-9.872-24.695L467.29 655.23H294.592l-19.737 54.33c-9.866 19.755-14.805 34.572-19.738 44.45-4.939 9.878-14.804 9.878-29.603 9.878-9.871 0-19.737-4.939-29.609-9.878-9.865-9.878-14.798-14.817-14.798-24.695 0-4.939 0-9.878 4.933-19.756 4.939-9.878 4.939-14.817 9.865-24.695l108.553-276.583c4.939-9.878 4.939-19.756 9.872-29.633 4.932-9.878 9.865-19.756 14.798-24.695 4.939-4.94 9.872-14.817 19.737-19.756 9.872-4.94 19.738-4.94 29.61-4.94 9.865 0 19.73 0 29.603 4.94 9.865 4.939 14.804 9.878 19.737 19.756 4.933 4.939 9.866 14.817 14.798 24.695 4.94 9.877 9.872 19.755 14.805 34.572l108.553 271.644c9.865 19.756 14.804 34.573 14.804 44.451-4.939 4.94-9.872 14.817-14.804 24.695zm271.378-192.62c-54.273-19.756-93.748-44.451-128.29-74.085-34.536 34.573-78.943 59.268-133.223 74.085l-14.798-24.695c54.273-14.817 98.68-34.573 133.223-69.146-34.542-34.573-64.145-79.024-74.017-128.413h-49.34V319.38h133.228c-9.877-14.817-19.743-34.573-29.609-49.39l14.799-4.94c9.871 14.818 24.676 34.574 34.542 54.33h123.35v24.695h-49.34c-14.798 49.39-39.468 93.84-69.077 123.474 34.541 29.634 74.01 54.329 128.29 69.146l-19.738 24.695z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon light-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M512 256a42.667 42.667 0 0 0 42.667-42.667V128a42.667 42.667 0 0 0-85.334 0v85.333A42.667 42.667 0 0 0 512 256zm384 213.333h-85.333a42.667 42.667 0 0 0 0 85.334H896a42.667 42.667 0 0 0 0-85.334zM256 512a42.667 42.667 0 0 0-42.667-42.667H128a42.667 42.667 0 0 0 0 85.334h85.333A42.667 42.667 0 0 0 256 512zm9.387-298.667a42.667 42.667 0 0 0-59.307 62.72l61.44 59.307a42.667 42.667 0 0 0 31.147 11.947 42.667 42.667 0 0 0 30.72-13.227 42.667 42.667 0 0 0 0-60.16zm459.946 133.974a42.667 42.667 0 0 0 29.44-11.947l61.44-59.307a42.667 42.667 0 0 0-57.6-62.72l-61.44 60.587a42.667 42.667 0 0 0 0 60.16 42.667 42.667 0 0 0 28.16 13.227zM512 768a42.667 42.667 0 0 0-42.667 42.667V896a42.667 42.667 0 0 0 85.334 0v-85.333A42.667 42.667 0 0 0 512 768zm244.48-79.36a42.667 42.667 0 0 0-59.307 61.44l61.44 60.587a42.667 42.667 0 0 0 29.44 11.946 42.667 42.667 0 0 0 30.72-12.8 42.667 42.667 0 0 0 0-60.586zm-488.96 0-61.44 59.307a42.667 42.667 0 0 0 0 60.586 42.667 42.667 0 0 0 30.72 12.8 42.667 42.667 0 0 0 28.587-10.666l61.44-59.307a42.667 42.667 0 0 0-59.307-61.44zM512 341.333A170.667 170.667 0 1 0 682.667 512 170.667 170.667 0 0 0 512 341.333z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon link-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M460.8 584.533c17.067 17.067 17.067 42.667 0 59.734-17.067 17.066-42.667 17.066-59.733 0-85.334-85.334-85.334-217.6 0-302.934L554.667 192C640 110.933 776.533 110.933 857.6 196.267c81.067 81.066 81.067 213.333 0 294.4l-68.267 64c0-34.134-4.266-68.267-17.066-102.4l21.333-21.334c51.2-46.933 55.467-128 4.267-179.2s-128-55.466-179.2-4.266c-4.267 0-4.267 4.266-4.267 4.266L465.067 401.067c-51.2 51.2-51.2 132.266-4.267 183.466m123.733-183.466C601.6 384 627.2 384 644.267 401.067c85.333 85.333 85.333 217.6 0 302.933l-153.6 149.333C405.333 934.4 268.8 934.4 187.733 849.067c-81.066-81.067-81.066-213.334 0-294.4l68.267-64c0 34.133 4.267 72.533 17.067 102.4L251.733 614.4C204.8 665.6 204.8 746.667 256 793.6c51.2 46.933 123.733 46.933 174.933 0l149.334-149.333c51.2-51.2 51.2-128 0-179.2-12.8-17.067-17.067-46.934 4.266-64z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon lock-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M787.168 952.268H236.832c-30.395 0-55.033-24.638-55.033-55.033V429.45c0-30.395 24.638-55.034 55.033-55.034h82.55V264.35c0-106.38 86.238-192.618 192.618-192.618S704.618 157.97 704.618 264.35v110.066h82.55c30.395 0 55.033 24.639 55.033 55.034v467.785c0 30.395-24.639 55.033-55.033 55.033zM484.483 672.046v115.122h55.034V672.046c31.99-11.373 55.033-41.605 55.033-77.496 0-45.592-36.958-82.55-82.55-82.55s-82.55 36.958-82.55 82.55c0 35.89 23.042 66.123 55.033 77.496zM622.067 264.35c0-60.788-49.28-110.067-110.067-110.067s-110.067 49.28-110.067 110.067v110.066h220.135V264.35z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon next-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M906.772 512c0 4.772-2.211 9.267-5.99 12.175L524.257 813.66a15.37 15.37 0 0 1-18.616.092 15.368 15.368 0 0 1-5.038-17.91l75.714-191.672h-443.73c-8.488 0-15.36-6.881-15.36-15.36v-153.6c0-8.489 6.872-15.36 15.36-15.36h443.73l-75.714-191.682a15.358 15.358 0 0 1 5.048-17.91c5.51-4.158 13.128-4.137 18.606.092l376.525 289.485a15.323 15.323 0 0 1 5.99 12.165z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="page-404-icon"
viewBox="0 0 178 130"
>
<defs>
<linearGradient
id="b"
x1=".5"
x2=".5"
y2="1"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#e9e9e9" />
<stop offset="1" stop-color="#fff" stop-opacity="0" />
</linearGradient>
<linearGradient
id="c"
x1=".5"
x2=".5"
y2="1"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#dcdcdc" />
<stop offset="1" stop-color="#fff" stop-opacity="0" />
</linearGradient>
<linearGradient
id="d"
x1=".5"
x2=".5"
y2="1"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#f1f1f1" />
<stop offset="1" stop-color="#fff" stop-opacity="0" />
</linearGradient>
<linearGradient
id="e"
x1=".5"
x2=".5"
y2="1"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#dedede" />
<stop offset="1" stop-color="#fff" stop-opacity="0" />
</linearGradient>
<linearGradient
id="f"
x1=".5"
x2=".5"
y2="1"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#e8e8e8" />
<stop offset="1" stop-color="#fff" stop-opacity="0" />
</linearGradient>
<linearGradient
id="g"
x1=".213"
y1="1.265"
x2=".846"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#fff" />
<stop offset="1" stop-color="#f5f5f5" />
</linearGradient>
<linearGradient
id="h"
x1=".5"
x2=".5"
y2="1"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#c5c5c5" />
<stop offset="1" stop-color="#fff" stop-opacity="0" />
</linearGradient>
<linearGradient
id="i"
x1=".5"
x2=".5"
y2="1"
gradientUnits="objectBoundingBox"
>
<stop offset="0" stop-color="#acacac" />
<stop offset="1" stop-color="#f2f2f2" stop-opacity=".388" />
</linearGradient>
<clipPath id="a">
<path transform="translate(744 1111)" fill="none" d="M0 0h178v130H0z" />
</clipPath>
</defs>
<g transform="translate(-744 -1111)" clip-path="url(#a)">
<g>
<path
d="M0 10.795 36.6 0v93.779L0 104.574z"
transform="translate(772.466 1122.142)"
fill="url(#b)"
/>
<path
d="M-8.492 10.642-26.361-.469v93.78l17.868 11.111z"
transform="translate(780.958 1122.293)"
fill="url(#c)"
/>
<path
d="M-8.5 5.55 28.106-5.3 10.228-16.437l-36.6 10.845z"
transform="translate(780.963 1127.438)"
fill="url(#d)"
/>
<path
d="M0 10.539 35.741 0v91.56L0 102.1z"
transform="translate(870.158 1123.617)"
fill="url(#d)"
/>
<path
d="M-8.913 10.38-26.361-.469v91.562l17.448 10.848z"
transform="translate(879.071 1123.775)"
fill="url(#e)"
/>
<path
d="m-8.918 5.032 35.741-10.59L9.366-16.437-26.375-5.848z"
transform="translate(879.076 1129.175)"
fill="url(#d)"
/>
<path
d="M0 9.137 30.839 0v79.381L0 88.519z"
transform="translate(799.678 1151.579)"
fill="url(#f)"
/>
<path
d="m-11.306 8.936-15.054-9.4v79.377l15.054 9.4z"
transform="translate(810.985 1151.78)"
fill="url(#c)"
/>
<path
d="M-11.313 2.087 19.526-7.05 4.464-16.437-26.375-7.3z"
transform="translate(810.991 1158.63)"
fill="url(#g)"
/>
<path
d="M178 53H0a51.361 51.361 0 0 1 10.453-20.952 74.532 74.532 0 0 1 19.742-16.811A103.3 103.3 0 0 1 57.089 4.058a127.515 127.515 0 0 1 63.823 0 103.3 103.3 0 0 1 26.894 11.179 74.532 74.532 0 0 1 19.741 16.811A51.363 51.363 0 0 1 178 53z"
transform="translate(744 1187.549)"
fill="url(#h)"
/>
<path d="m814.529 1199.586-1.272 1.212h2.3l1.2-1.212z" fill="#cbcbcb" />
<path
d="m816.725 1194.909-1.272 1.212h2.3l1.263-1.212z"
fill="#cbcbcb"
/>
<path d="m863.284 1199.585-1.272 1.212h2.3l1.2-1.212z" fill="#cbcbcb" />
<path d="m865.519 1194.9-1.272 1.212h2.3l1.263-1.212z" fill="#cbcbcb" />
<path
d="m799.527 1191.21 10.182-21.97h4.381l-9.931 21.719h14.876v3.941h-19.508zm13.081-9.493h4.152v17.859h-4.152zm20.728 18.151q-4.256 0-6.457-2.274a8.74 8.74 0 0 1-2.2-6.343v-13.791a8.708 8.708 0 0 1 2.21-6.353q2.212-2.264 6.447-2.264 4.256 0 6.457 2.253a8.726 8.726 0 0 1 2.2 6.363v13.792a8.708 8.708 0 0 1-2.21 6.349q-2.211 2.268-6.447 2.268zm0-4.048a4.29 4.29 0 0 0 3.328-1.178 4.862 4.862 0 0 0 1.074-3.39v-13.792a4.893 4.893 0 0 0-1.064-3.39 4.285 4.285 0 0 0-3.338-1.179 4.285 4.285 0 0 0-3.338 1.179 4.893 4.893 0 0 0-1.064 3.39v13.791a4.862 4.862 0 0 0 1.075 3.391 4.29 4.29 0 0 0 3.327 1.178zm14.928-4.61 10.181-21.97h4.381l-9.931 21.719h14.876v3.941h-19.507zm13.081-9.493h4.152v17.859h-4.152z"
fill="#c6c6c6"
/>
<path
d="m798.306 1192.431 10.182-21.97h4.381l-9.931 21.719h14.876v3.941h-19.508zm13.081-9.493h4.152v17.859h-4.152zm20.728 18.151q-4.256 0-6.457-2.274a8.74 8.74 0 0 1-2.2-6.343v-13.791a8.708 8.708 0 0 1 2.21-6.353q2.212-2.264 6.447-2.264 4.256 0 6.457 2.253a8.726 8.726 0 0 1 2.2 6.363v13.792a8.708 8.708 0 0 1-2.21 6.349q-2.211 2.268-6.447 2.268zm0-4.048a4.29 4.29 0 0 0 3.328-1.178 4.862 4.862 0 0 0 1.074-3.39v-13.792a4.893 4.893 0 0 0-1.064-3.39 4.285 4.285 0 0 0-3.338-1.179 4.285 4.285 0 0 0-3.338 1.179 4.893 4.893 0 0 0-1.064 3.39v13.791a4.862 4.862 0 0 0 1.075 3.391 4.29 4.29 0 0 0 3.327 1.178zm14.928-4.61 10.181-21.97h4.381l-9.931 21.719h14.876v3.941h-19.507zm13.081-9.493h4.152v17.859h-4.152z"
fill="#b2b2b2"
/>
<path
d="m-27.694-19.435 10.182 14.517h4.381l-9.931-14.352h14.876v-2.606h-19.508zm13.081 6.273h4.152v-11.8h-4.152zM6.115-25.156q-4.256 0-6.457 1.5a4.8 4.8 0 0 0-2.2 4.191v9.113a4.784 4.784 0 0 0 2.212 4.2 11.511 11.511 0 0 0 6.447 1.5q4.256 0 6.457-1.489a4.786 4.786 0 0 0 2.2-4.2v-9.113a4.784 4.784 0 0 0-2.212-4.2 11.511 11.511 0 0 0-6.447-1.502zm0 2.675a5.705 5.705 0 0 1 3.328.779 2.6 2.6 0 0 1 1.074 2.24v9.113a2.607 2.607 0 0 1-1.064 2.24 5.7 5.7 0 0 1-3.338.779 5.7 5.7 0 0 1-3.338-.779 2.607 2.607 0 0 1-1.064-2.24v-9.113A2.6 2.6 0 0 1 2.788-21.7a5.705 5.705 0 0 1 3.327-.782zm14.927 3.047L31.224-4.918h4.381l-9.931-14.351H40.55v-2.606H21.043zm13.081 6.273h4.152v-11.8h-4.151z"
transform="translate(826 1226.245)"
opacity=".32"
fill="url(#i)"
/>
<g fill="#e6e6e6">
<path d="m858.428 1169.23-1.2 1.259h4.388l1.178-1.259z" />
<path
d="m802.944 1192.187 1.288-1.375h7.143v1.375zm8.415-9.25 1.273-1.234h4.15l-1.235 1.234zm-2.855-12.469 1.198-1.259h4.367l-1.178 1.259z"
/>
<path d="m861.362 1181.678-1.27 1.3h4.188l1.236-1.3z" />
<path d="m865.519 1190.9-1.27 1.3h2.3l1.162-1.3z" />
<path d="m852.838 1190.791-1.207 1.508h8.447v-1.508z" />
</g>
</g>
</g>
</svg>
</template>
<style lang="stylus">
.page-404-icon
.theme-dark &
filter invert(70%)
</style>
<template>
<svg
class="icon presentation-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M896 170.667v426.666a85.333 85.333 0 0 1-85.333 85.334h-256v61.184l192.597 115.584-43.861 73.13-148.736-89.173v95.275h-85.334v-95.318l-148.736 89.216-43.861-73.13 192.597-115.627v-61.141h-256A85.333 85.333 0 0 1 128 597.333V170.667H85.333V85.333h853.334v85.334H896zm-682.667 0v426.666h597.334V170.667H213.333zM426.667 512h-85.334V341.333h85.334V512zm128 0h-85.334V256h85.334v256zm128 0h-85.334V384h85.334v128z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon prev-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M906.783 588.79c-.02 8.499-6.882 15.36-15.38 15.37l-443.7-.01 75.704 191.682c2.52 6.42.482 13.763-5.038 17.91-5.52 4.168-13.138 4.147-18.616-.092L123.228 524.175a15.362 15.362 0 0 1-6-12.165c0-4.782 2.222-9.277 6-12.185L499.753 210.35a15.388 15.388 0 0 1 9.38-3.195c3.236 0 6.502 1.034 9.236 3.103 5.52 4.147 7.578 11.49 5.038 17.91L447.683 419.84l443.72-.01c8.498.01 15.36 6.881 15.36 15.36l.02 153.6z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg class="icon" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path
d="M987.456 425.152H864V295.296a36.48 36.48 0 0 0-36.544-36.544h-360l-134.08-128.256A9.344 9.344 0 0 0 327.04 128H36.48A36.48 36.48 0 0 0 0 164.544v676.608a36.48 36.48 0 0 0 36.544 36.544h797.76a36.672 36.672 0 0 0 33.92-22.848L1021.44 475.52a36.48 36.48 0 0 0-33.92-50.304zM82.304 210.304h215.424l136.64 130.752h347.328v84.096H198.848A36.672 36.672 0 0 0 164.928 448L82.304 652.8V210.304zM808.32 795.456H108.544l118.08-292.608h699.904L808.32 795.52z"
fill="currentColor"
/>
</svg>
</template>
<template>
<svg
class="icon sticky-icon"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M989.922 444.341 580.15 34.909H119.411l870.511 870.597V444.34z" />
<path
class="sticky-text"
d="m621.368 219.398-43.833-43.77-126.663 126.841-32.827-32.78L544.71 142.846l-43.735-43.674 26.739-26.775L648.11 192.621l-26.741 26.776zm-38.762 65.278c24.03-24.065 50.614-36.637 79.751-37.716 29.135-1.077 55.24 9.904 78.314 32.945 21.95 21.919 32.324 46.87 31.121 74.852s-13.258 53.441-36.167 76.383c-23.901 23.935-50.255 36.407-79.057 37.416-28.807 1.013-54.482-9.74-77.025-32.252-22.016-21.985-32.69-47.068-32.015-75.244.673-28.18 12.366-53.639 35.078-76.384zm36.196 32.578c-14.921 14.943-23.517 30.756-25.783 47.439-2.27 16.684 2.88 31.298 15.441 43.842 12.826 12.807 27.348 18.234 43.567 16.271 16.217-1.96 31.986-10.608 47.303-25.948 15.977-15.998 25.133-32.11 27.467-48.332 2.334-16.221-2.813-30.637-15.442-43.247-12.827-12.81-27.679-18.133-44.558-15.973-16.879 2.158-32.878 10.809-47.995 25.948zm161.326 207.05-53.477 53.554-32.727-32.681L847.325 391.56l52.859 52.784c38.214 38.16 41.146 73.44 8.797 105.834-15.713 15.737-34.076 22.586-55.087 20.552-21.012-2.032-39.98-11.898-56.905-29.591l-16.861-16.834zm74.572-74.676-49.517 49.586 14.182 14.162c19.24 19.211 37.21 20.455 53.914 3.728 16.305-16.33 14.941-34.002-4.1-53.016l-14.479-14.46z"
/>
</svg>
</template>
<template>
<svg
class="icon icon-baidu"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#1D2FE3" />
<path
d="M239.022 704.978c.098-4.865-.314-9.772.162-14.591 5.178-52.464 197.571-253.377 249.641-259.233 42.996-4.833 75.768 16.545 99.824 49.144 37.893 51.351 82.81 95.455 131.292 136.237 52.903 44.503 56.525 99.801 32.6 158.592-23.425 57.56-75.34 69.833-127.771 58.804-84.971-17.874-168.158-13.744-253.37-4.536-86.35 9.333-133.788-39.4-132.378-124.417zM352.464 412.86c-3.58 50.707-17.93 96.128-75.9 98.12-58.053 1.995-80.093-41.432-79.275-91.71.81-49.705 13.416-104.053 76.851-102.136 53.84 1.625 74.74 45.8 78.324 95.726zm386.053 142.168c-68.494-1.735-84.188-43.331-82.635-93.812 1.46-47.519 10.082-97.628 73.299-96.65 61.395.95 81.6 43.207 81.553 98.668-.047 53.156-19.818 89.398-72.217 91.794zm-45.235-278.345c-10.464 42.665-24.513 91.761-85.919 94.502-52.74 2.354-71.705-34.482-72.805-81.242-1.233-52.42 48.08-112.965 87.582-110.373 33.943 2.226 71.146 49.541 71.142 97.113zm-195.147-14.097c-7.005 46.274-13.63 100.025-71.562 101.351-57.077 1.306-73.567-47.922-73.638-97.109-.068-48.054 12.128-99.024 69.345-101.426 59.45-2.493 67.11 51.093 75.855 97.184z"
fill="#fff"
/>
<path
d="M479.52 663.165c.006 12.194 1.498 24.61-.284 36.537-4.707 31.503 18.862 78.749-45.326 77.534-54.226-1.027-103.338-3.31-113.231-73.536-7.164-50.852 7.78-85.674 57.687-102.668 17.67-6.016 39.618 5.058 54.096-14.548 10.84-14.679-2.901-54.592 33.418-41.47 24.075 8.7 11.477 38.922 13.278 59.652 1.68 19.366.359 38.99.363 58.5zm175.45 41.902c4.291 39.657 5.093 78.047-64.709 73.503-60.097-3.912-95.56-20.794-86.293-85.624 4.287-29.991-21.148-83.238 22.19-84.867 42.71-1.606 13.57 50.41 20.825 77.622 5.276 19.794-3.984 46.774 29.753 48.193 41.337 1.738 28.383-30.022 31.099-51.604 1.209-9.61-.85-19.65.528-29.215 2.516-17.474-8.928-44.716 19.554-47.191 36.044-3.133 24.155 28.376 26.678 47.523 1.896 14.387.375 29.225.375 51.66z"
fill="#1D2FE3"
/>
<path
d="M435.669 685.038c-2.255 24.07 5.605 53.68-33.623 52.136-34.594-1.362-35.274-31.818-38.513-53.078-4.028-26.448 11.38-48.18 40.785-50.023 40.967-2.564 27.097 30.764 31.35 50.965z"
fill="#fff"
/>
</svg>
</template>
<template>
<svg
class="icon icon-bitbucket"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#205081" />
<path
d="M512 191.32v.01-.01c-152.76 0-277.805 41.114-277.805 92.166 0 13.443 33.353 206.247 46.577 282.708 5.932 34.285 94.508 84.563 231.126 84.563l.102-.407v.407c137.484 0 225.26-50.278 231.192-84.578 13.23-76.457 46.592-269.255 46.592-282.698.005-51.047-125.024-92.165-277.784-92.165zm0 397.493c-48.771 0-88.31-39.545-88.31-88.31 0-48.772 39.539-88.306 88.31-88.306s88.31 39.534 88.31 88.31c0 48.766-39.539 88.306-88.31 88.306zm-.05-276.842c-98.256-.153-177.885-17.232-177.855-38.14.036-20.912 79.72-37.731 177.976-37.568 98.256.153 177.884 17.22 177.849 38.139-.026 20.908-79.705 37.716-177.966 37.564z"
fill="#fff"
/>
<path
d="M711.668 642.814c-4.227 0-7.608 2.994-7.608 2.994S635.65 699.987 512 699.987s-192.06-54.18-192.06-54.18-3.386-2.988-7.608-2.988c-5.04 0-9.827 3.391-9.827 10.871 0 .79.076 1.579.224 2.353 10.617 56.826 18.382 97.206 19.736 103.347 9.268 41.805 91.045 73.411 189.525 73.411h.01c98.49 0 180.267-31.606 189.535-73.411 1.364-6.136 9.114-46.49 19.736-103.317.143-.779.224-1.578.224-2.368 0-7.485-4.786-10.881-9.827-10.881zM467.659 500.477a44.255 44.255 0 1 0 88.51 0 44.255 44.255 0 1 0-88.51 0z"
fill="#fff"
/>
</svg>
</template>
<template>
<svg
class="icon icon-dingding"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#1C9DF7" />
<path
d="M760.551 442.095c0 5.178-5.178 12.945-7.767 20.713-23.302 49.192-82.85 144.988-82.85 144.988l-18.124 31.069h85.44L576.727 853.758l36.247-144.988h-67.316l23.302-95.796c-18.124 5.178-41.426 10.356-67.316 18.124 0 0-36.247 20.712-100.974-38.837 0 0-44.015-38.836-18.124-49.192 10.356-5.178 54.37-10.356 88.029-12.945 44.014-5.179 72.494-10.357 72.494-10.357s-139.81 2.59-173.468-2.589c-33.658-5.178-75.083-59.549-82.85-108.741 0 0-12.946-25.89 28.48-12.945 44.013 12.945 222.66 49.192 222.66 49.192s-235.606-72.494-251.14-90.618c-15.535-18.123-46.604-95.796-41.426-144.988 0 0 2.59-12.945 12.945-7.767 0 0 173.469 80.261 292.566 121.686 119.098 41.426 222.66 64.727 209.715 119.098z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-discord"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#2DAAE1" />
<path
d="m422.935 240.147 5.851 8.052c-113.039 34.253-165.649 84.585-165.649 84.585s13.645-8.052 37.024-18.123c66.28-30.215 118.865-38.267 142.244-40.286 3.91 0 7.819-1.994 9.76-1.994 38.966-6.058 81.816-6.058 126.658-2.02 58.462 8.053 122.749 24.157 187.061 62.423 0 0-48.7-48.338-155.862-82.566l7.767-10.071h1.968c13.308.233 91.757 4.246 173.416 68.481 0 0 89.634 169.144 89.634 376.555 1.942-2.02-50.668 92.637-189.003 96.65 0 0-23.405-28.194-40.933-52.35 81.84-24.157 113.013-76.534 113.013-76.534a313.796 313.796 0 0 1-72.106 38.267c-31.172 14.11-60.403 22.162-89.633 28.22-60.404 12.066-114.955 8.027-161.74 0a636.81 636.81 0 0 1-91.576-28.22c-13.644-6.033-29.23-12.065-44.817-22.137-1.941-2.02-3.883-2.02-5.85-4.039-1.943 0-1.943-2.02-1.943-2.02-11.702-6.032-17.528-10.07-17.528-10.07s29.23 52.376 109.104 76.532c-19.47 24.157-40.907 54.371-40.907 54.371-138.36-4.039-190.97-98.67-190.97-98.67 0-207.41 89.633-376.555 89.633-376.555 89.634-70.5 175.384-68.481 175.384-68.481zm213.961 233.017c-35.315 0-64.727 34.512-64.727 77.672s29.412 77.672 64.727 77.672 64.727-34.512 64.727-77.672-29.412-77.672-64.727-77.672zm-233.016 0c-35.315 0-64.727 34.512-64.727 77.672s29.412 77.672 64.727 77.672 64.726-34.512 64.726-77.672-29.411-77.672-64.726-77.672z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-dribbble"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#DC4373" />
<path
d="M716.668 302.752c-13.332 5.154-28.61 3.676-35.726-1.068-86.196-48.27-179.286-62.06-265.482-37.926C292.074 300.212 241.674 437.028 242 564.746c22.622 42.136 52.558 105.234 61.854 153.104l298.24 298.246c210.532-37.376 376.584-203.408 413.986-413.928L716.668 302.752z"
fill="#C13366"
/>
<path
d="M511.994 219.308c-161.592 0-293.058 131.31-293.058 292.696s131.466 292.684 293.058 292.684c161.598 0 293.07-131.292 293.07-292.684 0-161.386-131.472-292.696-293.07-292.696zm192.842 138.148c33.43 41.432 53.744 93.756 54.682 150.772-11.122-2.282-58.23-11.068-114.526-11.068-18.164 0-37.282.91-56.448 3.278a822.854 822.854 0 0 0-4.938-11.812c-4.972-11.692-10.326-23.29-15.874-34.72 86.642-35.696 128.436-85.238 137.104-96.45zm-192.842-92.33c62.446 0 119.528 23.2 163.126 61.412-6.902 9.416-44.332 56.202-127.876 87.768-38.67-70.688-80.996-129.22-91.07-142.812a247.13 247.13 0 0 1 55.82-6.368zm-106.352 23.99c8.536 11.758 50.928 70.918 90.592 141.036-106.448 27.982-200.976 29.806-223.79 29.806h-2.424c16.39-75.534 67.424-138.298 135.622-170.842zM264.402 512.39c0-2.038.034-4.076.1-6.102 1.48.018 3.666.018 6.5.018 30.726 0 137.382-2.538 247.288-35.154a828.402 828.402 0 0 1 18.928 39.526 232.24 232.24 0 0 0-8.234 2.482C405.236 553.126 337.216 658.936 326.75 676.232c-38.778-43.696-62.348-101.058-62.348-163.842zm247.592 247.246c-56.786 0-109.192-19.232-151.01-51.48 7.074-13.868 58.412-106.3 194.026-153.5a1.806 1.806 0 0 1 .272-.09c34.006 88.53 48.408 162.834 52.358 185.862-29.432 12.374-61.752 19.208-95.646 19.208zm140.236-43.584c-3.32-19.052-16.66-88.688-47.452-173.618 17.27-2.686 34.072-3.748 49.828-3.748 51.748 0 92.23 11.33 101.634 14.204-11.182 67.444-49.794 125.808-104.01 163.162z"
fill="#F0F1F1"
/>
<path
d="M511.994 219.308c-.382 0-.758.028-1.142.028v45.804c.38 0 .758-.018 1.142-.018 62.446 0 119.528 23.2 163.126 61.412-6.902 9.416-44.332 56.202-127.876 87.768-12.14-22.194-24.642-43.188-36.392-61.968V473.29c2.476-.706 4.954-1.41 7.434-2.144a828.402 828.402 0 0 1 18.928 39.526c-2.776.796-5.514 1.592-8.234 2.478a323.314 323.314 0 0 0-18.128 6.52v53.228a418.976 418.976 0 0 1 44.428-18.332c34.006 88.53 48.408 162.834 52.358 185.862-29.428 12.374-61.746 19.208-95.646 19.208-.382 0-.758-.024-1.142-.024v45.05c.382 0 .758.028 1.142.028 161.598 0 293.07-131.292 293.07-292.684.002-161.388-131.47-292.698-293.068-292.698zm71.614 269.316c-4.972-11.692-10.326-23.29-15.874-34.72 86.64-35.696 128.434-85.236 137.102-96.45 33.43 41.432 53.748 93.756 54.682 150.77-11.122-2.282-58.23-11.068-114.522-11.068-18.164 0-37.282.914-56.448 3.278-1.624-3.932-3.24-7.852-4.94-11.81zm68.622 227.428c-3.32-19.052-16.66-88.688-47.452-173.618 17.27-2.686 34.072-3.748 49.828-3.748 51.748 0 92.23 11.33 101.634 14.204-11.182 67.444-49.794 125.808-104.01 163.162z"
fill="#D1D1D1"
/>
</svg>
</template>
<template>
<svg
class="icon icon-email"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M0 512a512 512 0 101024 0A512 512 0 100 512z" fill="#1384FF" />
<path
d="M299.372 313.572H722.93c28.945 0 52.61 21.845 52.975 48.787L511.333 500.35 246.76 362.481c.182-27.003 23.666-48.97 52.611-48.97zm-52.671 101.702l-.243 244.121c0 27.186 23.848 49.395 52.914 49.395H722.93c29.127 0 52.975-22.21 52.975-49.395V415.152L517.522 546.71a13.957 13.957 0 01-12.682 0L246.7 415.274z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-evernote"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#36D613" />
<path d="M347.671 193.259v87.51h-87.51z" fill="#595757" />
<path
d="M788.047 323.645s3.65-77.491-73.84-103.02c0 0-89.479-12.867-151.41-11.91 0 0-8.595-53.23-103.33-53.23 0 0-89.556-1.244-89.892 70.526v61.671s2.848 14.991-27.833 14.991h-81.581s-34.28 5.282-34.28 72.934c0 0 3.133 120.082 41.322 200.24 0 0 9.398 34.667 58.228 46.577 0 0 95.822 25.477 123.991 21.722 0 0 58.228 22.137 62.008-111.874 0 0 3.755-19.935 6.266 11.392 0 0-1.89 68.948 57.607 72.702 0 0 45.723 12.557 73.892 10.045 0 0 37.568 2.15 37.568 64.158 0 0 13.152 71.665-34.435 71.665h-65.763s-18.149 4.428-18.149-21.877c0 0-4.997-21.878 26.305-21.878h15.534v-43.756h-43.082s-66.332-6.317-66.332 50.047v75.135s9.347 49.866 66.332 49.866h121.273s48.441.44 76.61-90.359c0-.078 48.52-182.323 22.991-435.767zM625.272 486.523c0-21.877 18.02-51.16 39.432-51.16s36.48 36.118 36.48 58.022c-28.79-7.897-45.827-9.606-75.912-6.862z"
fill="#595757"
/>
</svg>
</template>
<template>
<svg
class="icon icon-facebook"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#3C599B" />
<path
d="M372.568 413.895h59.898V355.68c0-25.67.647-65.257 19.294-89.774 19.642-25.965 46.605-43.613 92.983-43.613 75.565 0 107.384 10.778 107.384 10.778l-14.971 88.74s-24.967-7.217-48.254-7.217c-23.302 0-44.16 8.35-44.16 31.635v67.666h95.526l-6.67 86.678h-88.855V801.69H432.466V500.574h-59.898v-86.68z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-flipboard"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#E12828" />
<path d="M293.58 292.18h160.343v481.003H293.58V292.18z" fill="#FFF" />
<path d="M453.922 292.18h320.662v160.343H453.922V292.18z" fill="#FCE9E9" />
<path
d="M453.922 452.523h160.343v160.343H453.922V452.523z"
fill="#F6BEBE"
/>
</svg>
</template>
<template>
<svg
class="icon icon-gitee"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#C71D23" />
<path
d="M772.953 454.723H480.17v.006a25.46 25.46 0 0 0-25.46 25.453l-.025 63.649a25.46 25.46 0 0 0 25.46 25.466l178.242-.007a25.46 25.46 0 0 1 25.459 25.46v12.73c0 42.18-34.198 76.378-76.378 76.378H365.583a25.46 25.46 0 0 1-25.46-25.46V416.533h-.006c0-42.18 34.192-76.378 76.378-76.378h356.388v-.013a25.46 25.46 0 0 0 25.46-25.446l.057-63.65h.013a25.46 25.46 0 0 0-25.46-25.471l-356.432.012c-105.453 0-190.946 85.493-190.946 190.946v356.433a25.46 25.46 0 0 0 25.46 25.46H626.56c94.913 0 171.852-76.94 171.852-171.852V480.182a25.46 25.46 0 0 0-25.46-25.46z"
fill="#fff"
/>
</svg>
</template>
<template>
<svg
class="icon icon-github"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#171515" />
<path
d="M509.423 146.442c-200.317 0-362.756 162.42-362.756 362.8 0 160.266 103.936 296.24 248.109 344.217 18.139 3.327 24.76-7.872 24.76-17.486 0-8.613-.313-31.427-.49-61.702-100.912 21.923-122.205-48.63-122.205-48.63-16.495-41.91-40.28-53.067-40.28-53.067-32.937-22.51 2.492-22.053 2.492-22.053 36.407 2.566 55.568 37.386 55.568 37.386 32.362 55.438 84.907 39.43 105.58 30.143 3.296-23.444 12.667-39.43 23.032-48.498-80.557-9.156-165.246-40.28-165.246-179.297 0-39.604 14.135-71.988 37.342-97.348-3.731-9.178-16.18-46.063 3.556-96.009 0 0 30.46-9.754 99.76 37.19 28.937-8.048 59.97-12.071 90.823-12.211 30.807.14 61.843 4.165 90.822 12.21 69.26-46.944 99.663-37.189 99.663-37.189 19.792 49.946 7.34 86.831 3.61 96.01 23.25 25.359 37.29 57.742 37.29 97.347 0 139.366-84.82 170.033-165.637 179.013 13.026 11.2 24.628 33.342 24.628 67.182 0 48.498-.445 87.627-.445 99.521 0 9.702 6.535 20.988 24.945 17.444 144.03-48.067 247.881-183.95 247.881-344.175 0-200.378-162.442-362.798-362.802-362.798z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-gitlab"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#E8F0FF" />
<path d="m512 848.182 134.473-413.8H377.527L512 848.182z" fill="#E24329" />
<path d="m512 848.182-134.473-413.8h-188.36L512 848.182z" fill="#FC6D26" />
<path
d="m189.167 434.382-40.787 125.82a27.8 27.8 0 0 0 10.135 31.147L512 848.182l-322.833-413.8z"
fill="#FCA326"
/>
<path
d="M189.167 434.382h188.36l-80.832-249.17c-4.202-12.854-22.247-12.854-26.45 0l-81.078 249.17z"
fill="#E24329"
/>
<path d="m512 848.182 134.473-413.8h188.36L512 848.182z" fill="#FC6D26" />
<path
d="m834.833 434.382 40.787 125.82a27.8 27.8 0 0 1-10.135 31.147L512 848.182l322.833-413.8z"
fill="#FCA326"
/>
<path
d="M834.833 434.382h-188.36l81.079-249.17c4.202-12.854 22.247-12.854 26.45 0l80.831 249.17z"
fill="#E24329"
/>
</svg>
</template>
<template>
<svg
class="icon icon-gmail"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#DB4437" />
<path d="M277.48 285.567h465.767v441.362H277.48V285.567z" fill="#E67C73" />
<path
d="M282.543 285.567h-10.645c-25.962 0-47.122 21.808-47.122 48.705v343.952c0 26.897 21.08 48.705 47.122 48.705h24.976V407.954l213.49 169.95 213.489-169.95V726.93h24.975c26.04 0 47.123-21.809 47.123-48.705V334.272c0-26.897-21.134-48.705-47.123-48.705h-10.644L510.364 480.44 282.542 285.567z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-instagram"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#181818" />
<path
d="M512 348.16c-88.222 0-163.84 71.417-163.84 163.84 0 88.222 71.417 163.84 163.84 163.84 88.222 0 163.84-71.417 163.84-163.84 0-88.222-75.618-163.84-163.84-163.84zm0 268.866c-58.814 0-105.026-46.212-105.026-105.026S453.186 406.974 512 406.974 617.026 453.186 617.026 512 570.814 617.026 512 617.026zM680.041 306.15c-21.005 0-37.81 16.804-37.81 37.809s16.805 37.81 37.81 37.81 37.81-16.805 37.81-37.81-16.805-37.81-37.81-37.81z"
fill="#FFF"
/>
<path
d="M659.036 196.923h-16.804c-50.413-4.2-210.051-4.2-260.464 0-96.623-4.2-180.644 71.418-184.845 168.041v16.804c-4.2 50.413-4.2 210.051 0 260.464-4.2 96.623 71.418 180.644 168.041 184.845h16.804c50.413 4.2 210.051 4.2 260.464 0 96.623 4.2 180.644-71.418 184.845-168.041V381.768c4.2-96.623-71.418-180.644-168.041-184.845zM759.86 696.845c-12.604 29.407-33.609 50.412-58.815 58.814-121.83 16.805-247.86 16.805-373.891 0-29.407-12.603-50.412-33.608-58.814-58.814-12.604-63.015-16.805-126.03-12.604-184.845-4.2-63.015 0-126.03 12.604-184.845 12.603-29.407 33.608-50.412 58.814-58.814 121.83-16.805 247.86-16.805 373.891 0 29.407 12.603 50.412 33.608 58.815 58.814 12.603 63.015 16.804 126.03 12.603 184.845 4.2 63.015 0 126.03-12.603 184.845z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-line"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#00C300" />
<path
d="M861.44 469.76C861.44 313.28 704 186.08 512 186.08s-349.44 127.2-349.44 283.68C162.56 608 286.88 727.52 454.88 752c11.52 2.4 26.88 7.68 30.72 17.28a71.04 71.04 0 0 1 0 31.68l-5.28 29.76c0 8.64-7.2 34.56 30.24 18.72a1104 1104 0 0 0 274.56-202.56A251.52 251.52 0 0 0 860 472.16zM375.2 562.88h-69.12a17.76 17.76 0 0 1-18.24-18.24v-139.2a17.76 17.76 0 0 1 18.24-18.24 18.24 18.24 0 0 1 18.24 18.24v120.48h50.88a18.72 18.72 0 0 1 18.24 18.72 18.24 18.24 0 0 1-18.24 18.24zm72-18.24a18.24 18.24 0 1 1-36.48 0v-139.2a18.24 18.24 0 0 1 36.48 0zm167.04 0a18.24 18.24 0 0 1-12.48 17.28H596a18.24 18.24 0 0 1-14.4-7.2l-69.6-96v85.92a18.24 18.24 0 1 1-36.48 0v-139.2A18.24 18.24 0 0 1 488 388.16h5.76a18.24 18.24 0 0 1 14.4 7.2l71.52 96v-85.92a18.24 18.24 0 1 1 36.48 0zm112.32-87.84a18.24 18.24 0 0 1 18.24 18.24 17.76 17.76 0 0 1-18.24 18.24h-50.88v32.64h50.88a18.72 18.72 0 0 1 18.24 18.72 18.24 18.24 0 0 1-18.24 18.24H656a18.24 18.24 0 0 1-18.24-18.24v-139.2A18.24 18.24 0 0 1 656 387.2h69.12a18.24 18.24 0 0 1 18.24 18.24 18.24 18.24 0 0 1-18.24 18.72h-49.44v32.64zm0 0"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-linkedin"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
width="240"
height="240"
>
<circle cx="512" cy="512" r="512" fill="#4376B1" />
<path
d="M838.301 555.94v225.157h-130.54V571.03c0-52.746-18.847-88.766-66.112-88.766-36.069 0-57.496 24.25-66.959 47.732-3.436 8.391-4.322 20.045-4.322 31.814v219.277h-130.55s1.752-355.784 0-392.613h130.56v55.637c-.263.438-.633.867-.867 1.285h.866v-1.285c17.349-26.694 48.287-64.856 117.651-64.856 85.884 0 150.273 56.114 150.273 176.685zm-535.05-356.72c-44.655 0-73.87 29.314-73.87 67.826 0 37.695 28.368 67.855 72.157 67.855h.847c45.532 0 73.842-30.16 73.842-67.855-.866-38.512-28.31-67.825-72.975-67.825zM237.14 781.098h130.5V388.474h-130.5v392.623z"
fill="#F1F2F2"
/>
</svg>
</template>
<template>
<svg
class="icon icon-pinterest"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M512 1023.147c282.773 0 512-228.288 512-509.888 0-281.622-229.227-509.91-512-509.91S0 231.637 0 513.26c0 281.6 229.227 509.888 512 509.888z"
fill="#FFF"
/>
<path
d="M512 3.35C229.248 3.35 0 231.658 0 513.258c0 216.128 134.848 400.789 325.312 475.05-4.63-40.277-8.427-102.378 1.685-146.453 9.28-39.872 59.84-253.483 59.84-253.483s-15.168-30.634-15.168-75.541c0-70.933 41.302-123.797 92.715-123.797 43.819 0 64.896 32.725 64.896 71.765 0 43.627-27.819 109.099-42.56 169.963-12.224 50.773 25.707 92.33 75.84 92.33 91.03 0 160.981-95.68 160.981-233.344 0-122.133-88.064-207.317-214.058-207.317-145.814 0-231.36 108.693-231.36 221.163 0 43.648 16.853 90.645 37.93 116.245a15.19 15.19 0 0 1 3.371 14.699c-3.797 15.936-12.65 50.773-14.336 57.92-2.09 9.216-7.573 11.328-17.28 6.698-64.043-29.781-104.085-122.538-104.085-197.653 0-160.747 117.162-308.459 338.389-308.459 177.408 0 315.627 125.888 315.627 294.614 0 175.829-111.254 317.269-265.472 317.269-51.84 0-100.715-26.859-117.163-58.752l-32.021 121.28c-11.371 44.48-42.56 99.883-63.638 133.867A516.01 516.01 0 0 0 511.168 1024c282.752 0 512-228.31 512-509.91C1024 231.66 794.752 3.35 512 3.35z"
fill="#CA242D"
/>
</svg>
</template>
<template>
<svg
class="icon icon-pocket"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#EE4056" />
<path
d="M716.52 309.066c12.549 0 23.172 4.394 31.87 13.182 8.697 8.788 13.023 19.48 13.023 32.006v150.4c0 33.975-6.568 66.41-19.705 97.307-13.138 30.918-30.76 57.487-52.89 79.685-22.106 22.197-48.562 39.864-79.367 52.888-30.804 13.024-63.081 19.547-96.876 19.547a246.897 246.897 0 0 1-97.215-19.547c-30.805-13.046-57.306-30.668-79.504-52.888-22.198-22.198-39.865-48.767-53.003-79.663a246.311 246.311 0 0 1-19.728-97.33V354.255c0-12.321 4.44-22.945 13.319-31.847a43.489 43.489 0 0 1 31.87-13.341H716.52zM512.574 617.339c9.06 0 16.989-3.216 23.738-9.581l117.103-112.415a32.622 32.622 0 0 0 10.691-24.62c0-9.469-3.33-17.533-9.966-24.191a32.958 32.958 0 0 0-24.237-10.012c-9.06 0-16.988 3.171-23.737 9.56l-93.547 89.808-93.614-89.809a33.185 33.185 0 0 0-23.443-9.559c-9.468 0-17.532 3.33-24.19 9.967-6.66 6.682-9.967 14.722-9.967 24.236 0 9.83 3.443 18.03 10.419 24.599l117.33 112.413c6.342 6.342 14.179 9.56 23.466 9.56l-.046.044z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-qq"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#5EAADE" />
<path
d="M729.46 627.3c-3.157-39.628-24.045-83.747-32.624-105.91l-22.084-57.047c-.702-23.73 6.312-78.322-30.511-146.61s-110.82-74.446-124.497-75.147c-13.677-.701-99.248-1.403-141.331 72.945-42.084 74.347-30.745 148.812-30.745 148.812l-23.523 57.478c-.001.002-10.962 26.223-20.43 58.135-9.469 31.914-18.938 82.064-9.469 92.234 9.47 10.17 43.837-46.643 46.993-51.903 0 0 2.456 27.18 8.943 41.383l.81 1.776.33.723.38.826.3.652.444.96.203.436a281.465 281.465 0 0 0 1.917 4.025l.189.386c.231.473.468.953.711 1.442l.146.292c6.886 13.807 18.61 33.823 37.443 50.42l.018.016-1.184.387c-10.667 3.516-31.694 11.21-40.625 19.82-1.717 1.655-2.987 3.344-3.65 5.045-5.376 13.794 4.208 15.43 20.575 16.366 16.366.934 94.923 3.04 132.564-2.221.407-.056.787-.114 1.17-.171 2.711.094 5.324.142 7.83.16l.151.002c.836.005 1.663.008 2.475.008.496 0 1.015-.002 1.542-.006l.21-.001a222.593 222.593 0 0 0 5.462-.107c.26.038.508.076.778.114 37.642 5.26 116.198 3.156 132.564 2.22 16.366-.934 25.951-2.571 20.574-16.365-4.302-11.037-34.175-21.62-45.956-25.413a141.388 141.388 0 0 0 7.958-7.645l.237-.245a142.494 142.494 0 0 0 2.53-2.702c42.435-46.643 38.928-76.101 40.682-92.935 0 0 35.775 51.553 43.488 53.306 7.713 1.754 10.169-6.31 7.012-45.94z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-qzone"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#0985DD" />
<path
d="M722.38 595.24c22.486-4.056 11.345-12.424 2.156-11.346-16.685 1.72-40.43 1.925-66.562 1.284l3.029 17.79a656.641 656.641 0 0 0 61.402-7.702l-.025-.026zm68.95-174.915a5.287 5.287 0 0 0-4.493-3.645L598.42 389.29l-84.326-170.628c-1.925-3.594-7.958-3.594-9.857 0L419.885 389.29l-188.417 27.39a5.338 5.338 0 0 0-4.466 3.645 5.493 5.493 0 0 0 1.488 5.57l136.36 132.92-32.088 187.519a5.263 5.263 0 0 0 2.13 5.39c1.695 1.284 3.851 1.463 5.776.385l168.651-88.407 168.524 88.638 2.567.642 3.209-1.079c1.72-1.283 2.566-3.208 2.13-5.34l-24.591-143.648c-27.21 2.156-54.37 3.183-76.42 3.183-77.267 0-135.075-3.645-135.948-3.645a16.48 16.48 0 0 1-14.785-11.757 16.247 16.247 0 0 1 5.981-17.764l155.431-113.05c-99.959-7.906-183.873-6.418-184.721-6.418-13.502.642-25.67-3.645.642-14.375 4.518-1.694 109.2-23.72 230.362-7.445 6.673.847 12.013 5.75 13.733 12.194a16.61 16.61 0 0 1-6.263 17.302L497.204 571.598c27.826 5.802 100.37 12.014 160.745 13.502l-4.519-26.312 136.308-132.97a5.338 5.338 0 0 0 1.54-5.544l.051.051z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-reddit"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#EB5528" />
<path
d="M617.199 680.55c5.666 5.974 5.666 11.742 0 17.34-21.845 23.143-56.9 34.714-105.199 34.714s-83.354-11.571-105.199-34.714c-5.666-5.598-5.666-11.366 0-17.34a10.445 10.445 0 0 1 7.919-3.379c3.174 0 5.803 1.127 7.919 3.38 16.93 18.295 46.728 27.408 89.361 27.408 42.325 0 72.09-9.113 89.361-27.409a10.445 10.445 0 0 1 7.919-3.379c3.174 0 5.803 1.127 7.919 3.38zm-176.06-136.635c9.182 9.694 13.756 21.47 13.756 35.294 0 13.79-4.574 25.565-13.756 35.26a44.134 44.134 0 0 1-33.28 14.54c-13.073 0-24.234-4.847-33.587-14.54a49.015 49.015 0 0 1-13.995-35.26c0-14.2 4.642-26.147 13.995-35.84 9.353-9.728 20.514-14.575 33.587-14.575 13.04 0 24.132 5.051 33.28 15.12zm222.584 35.294c0 13.79-4.642 25.565-13.995 35.26a44.954 44.954 0 0 1-33.587 14.54c-13.04 0-24.132-4.847-33.28-14.54a49.493 49.493 0 0 1-13.756-35.26c0-13.824 4.574-25.669 13.756-35.567 9.148-9.9 20.24-14.848 33.28-14.848 13.073 0 24.234 4.847 33.587 14.575 9.353 9.693 13.995 21.64 13.995 35.84zM796.433 512c0-18.295-6.144-33.963-18.5-47.036a59.494 59.494 0 0 0-44.92-19.592c-17.647 0-32.768 6.724-45.465 20.138-45.841-33.587-100.66-51.507-164.455-53.725l33.314-158.482 105.746 25.19c0 13.825 4.573 25.6 13.755 35.295 9.148 9.694 20.241 14.54 33.314 14.54 13.04 0 24.235-4.915 33.553-14.813 9.353-9.899 13.995-21.743 13.995-35.567s-4.642-25.669-13.995-35.567a44.578 44.578 0 0 0-33.553-14.814c-19.046 0-33.143 9.318-42.325 27.99L550.06 228.112c-6.69-1.877-11.094 1.126-13.21 8.977l-36.488 174.695c-63.454 2.594-117.897 20.718-163.363 54.272a59.187 59.187 0 0 0-46.011-20.685c-17.613 0-32.598 6.52-44.92 19.592a66.082 66.082 0 0 0-18.5 47.036c0 13.073 3.243 25.02 9.762 35.84 6.52 10.82 15.258 19.046 26.18 24.644a152.303 152.303 0 0 0-3.174 31.335c0 53.009 24.678 98.372 74.035 136.09 49.323 37.682 108.715 56.524 178.176 56.524 69.769 0 129.365-18.842 178.688-56.525 49.357-37.717 74.001-83.08 74.001-136.09 0-11.946-1.229-22.561-3.686-31.914 10.581-5.598 19.046-13.722 25.395-24.337 6.315-10.65 9.49-22.528 9.49-35.567z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-rss"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#FD9B00" />
<path
d="M687.981 740.39c0-225.92-183.617-409.777-409.21-409.777v-97.205c279.353 0 506.617 227.506 506.617 506.98H687.98zm-74.841 0h-97.538c0-63.567-24.688-123.245-69.43-167.993-44.762-44.856-104.24-69.556-167.54-69.556v-97.176c184.44 0 334.508 150.046 334.508 334.725zM346.038 605.166c37.35 0 67.514 30.357 67.514 67.39 0 37.146-30.163 67.177-67.514 67.177-37.219 0-67.458-30.03-67.458-67.176 0-37.034 30.24-67.391 67.458-67.391z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-steam"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cy="512" cx="512" fill="#fff" r="488" />
<path
d="M1008 512c0 274-222.4 496-496.8 496-227.6 0-419.2-152.6-478-360.8l190.4 78.6c12.8 64.2 69.8 112.8 137.8 112.8 78.4 0 143.8-64.8 140.4-147l169-120.4c104.2 2.6 191.6-81.8 191.6-187 0-103.2-84-187-187.4-187s-187.4 84-187.4 187v2.4L369.2 558c-31-1.8-61.4 6.8-87 24.2L16 472.2C36.4 216.8 250.2 16 511.2 16 785.6 16 1008 238 1008 512zM327.4 768.6l-61-25.2a105.58 105.58 0 0 0 54.4 51.6c53.8 22.4 115.6-3.2 138-56.8 10.8-26 11-54.6.2-80.6-10.8-26-31-46.4-57-57.2-25.8-10.8-53.4-10.4-77.8-1.2l63 26c39.6 16.4 58.4 61.8 41.8 101.4-16.6 39.8-62 58.4-101.6 42zM675 508.8c-68.8 0-124.8-56-124.8-124.6s56-124.6 124.8-124.6 124.8 56 124.8 124.6S744 508.8 675 508.8zm.2-31.2c51.8 0 93.8-42 93.8-93.6 0-51.8-42-93.6-93.8-93.6s-93.8 42-93.8 93.6c.2 51.6 42.2 93.6 93.8 93.6z"
fill="#13227a"
/>
</svg>
</template>
<template>
<svg
class="icon icon-twitter"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#5EAADE" />
<path
d="M749.737 364.631c-17.594 7.805-36.513 13.088-56.371 15.459 20.269-12.148 35.836-31.387 43.156-54.312A196.233 196.233 0 0 1 674.2 349.6c-17.894-19.083-43.406-30.997-71.636-30.997-54.2 0-98.137 43.944-98.137 98.157 0 7.695.861 15.19 2.544 22.373-81.57-4.092-153.876-43.174-202.284-102.558-8.443 14.498-13.285 31.356-13.285 49.348 0 34.05 17.326 64.096 43.656 81.697a97.69 97.69 0 0 1-44.447-12.277c-.01.41-.01.82-.01 1.24 0 47.558 33.822 87.23 78.72 96.249a98.285 98.285 0 0 1-25.852 3.448 97.491 97.491 0 0 1-18.465-1.768c12.483 39.002 48.725 67.38 91.672 68.17-33.582 26.334-75.897 42.024-121.884 42.024-7.924 0-15.736-.46-23.408-1.37 43.434 27.844 95.014 44.104 150.443 44.104 180.505 0 279.221-149.576 279.221-279.294 0-4.263-.09-8.494-.278-12.708 19.178-13.835 35.813-31.115 48.967-50.807z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-wechat"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#1AC88E" />
<path
d="M827.551 578.742a176.583 176.583 0 0 0-185.685-158.379 172.942 172.942 0 0 0-186.3 158.379 172.942 172.942 0 0 0 185.686 158.379 282.169 282.169 0 0 0 65.536-10.923l60.689 32.768-16.384-54.613a166.275 166.275 0 0 0 76.458-125.611zm-245.76-27.307a21.845 21.845 0 1 1 0-43.69 24.872 24.872 0 0 1 27.307 21.845 24.872 24.872 0 0 1-27.921 21.845h.614zm121.356 0a21.845 21.845 0 1 1 0-43.69 24.872 24.872 0 0 1 27.306 21.845 24.872 24.872 0 0 1-28.512 21.845h1.206z"
fill="#FFF"
/>
<path
d="M623.662 400.953h21.23A222.709 222.709 0 0 0 419.772 245.6a208.145 208.145 0 0 0-223.323 189.94 182.044 182.044 0 0 0 89.201 150.483l-22.436 67.356 78.279-39.435a389.575 389.575 0 0 0 78.279 10.923h20.616a163.226 163.226 0 0 1-6.667-46.718 182.044 182.044 0 0 1 189.94-177.197zm-121.379-60.69a27.921 27.921 0 1 1 0 55.843 31.562 31.562 0 0 1-33.36-27.921 31.562 31.562 0 0 1 34.59-27.921h-1.23zM346.34 396.107a31.562 31.562 0 0 1-33.383-27.921 31.562 31.562 0 0 1 33.383-27.921 27.921 27.921 0 1 1 0 55.842z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-weibo"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#E6162D" />
<path
d="M745.314 454.802c9.652 0 17.869-7.258 19.239-16.728a8.39 8.39 0 0 0 .261-2.12C779.445 302.233 657.02 325.25 657.02 325.25c-10.869 0-19.567 8.94-19.567 20.089 0 10.97 8.698 19.907 19.567 19.907 87.95-19.732 68.54 69.649 68.54 69.649-.004 11.06 8.842 19.908 19.754 19.908z"
fill="#fff"
/>
<path
d="M731.054 221.409c-42.342-10.077-85.862-1.393-98.055.981-.938.09-1.829.994-2.697 1.17-.415.088-.673.532-.673.532-12.045 3.457-20.828 14.77-20.828 28.14 0 15.932 12.694 29.034 28.564 29.034 0 0 15.39-2.097 25.846-6.252 10.364-4.246 98.012-3.16 141.576 71.17 23.734 54.247 10.428 90.553 8.778 96.387 0 0-5.653 14.095-5.653 27.973 0 16.024 12.694 26.083 28.433 26.083 13.169 0 24.211-1.821 27.452-24.447h.172c46.768-158.386-57.183-232.81-132.915-250.771zm-44.083 282.78c-28.28-5.579-14.519-21.062-14.519-21.062s27.67-46.38-5.482-80.099c-41.104-41.761-140.966 5.314-140.966 5.314-38.144 12.032-28.02-5.49-22.629-35.31 0-35.13-11.844-94.596-113.445-59.47-101.49 35.309-188.654 159.03-188.654 159.03-60.603 82.207-52.56 145.747-52.56 145.747 15.128 140.268 161.749 178.772 275.782 187.89 119.967 9.564 281.905-42.045 330.988-148.064 49.105-106.193-40.126-148.22-68.515-153.975zM433.387 766.675c-119.124 5.658-215.394-55.053-215.394-135.851 0-80.887 96.27-145.748 215.394-151.328 119.162-5.58 215.634 44.333 215.634 125.052.002 80.79-96.475 156.626-215.634 162.127z"
fill="#fff"
/>
<path
d="M409.603 532.773c-119.77 14.249-105.943 128.31-105.943 128.31s-1.22 36.117 32.126 54.513c70.084 38.593 142.248 15.224 178.723-32.634 36.474-47.888 15.086-164.346-104.906-150.189zM379.39 692.856c-22.343 2.665-40.385-10.437-40.385-29.463 0-18.94 16.02-38.768 38.387-41.143 25.694-2.485 42.431 12.56 42.431 31.588-.003 18.936-18.128 36.449-40.433 39.018zm70.626-61.146c-7.59 5.754-16.893 4.958-20.892-1.948-4.175-6.726-2.607-17.52 5.046-23.19 8.863-6.714 18.105-4.779 22.106 1.958 4.02 6.893 1.153 17.246-6.26 23.18z"
fill="#fff"
/>
</svg>
</template>
<template>
<svg
class="icon icon-whatsapp"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#31B84C" />
<path
d="m192.021 832 45.227-164.33a315.413 315.413 0 0 1-42.539-158.529C194.731 334.251 337.707 192 513.344 192c84.587-.213 165.76 33.28 225.387 93.013A314.453 314.453 0 0 1 832 509.376c-.085 174.848-143.04 317.141-318.656 317.141h-.15a319.61 319.61 0 0 1-152.277-38.613L192 832h.021zm241.686-455.467c-6.443-15.445-13.014-13.354-17.92-13.61-4.63-.214-9.942-.256-15.254-.256a29.227 29.227 0 0 0-21.226 9.898c-7.296 7.958-27.84 27.136-27.84 66.134s28.501 76.672 32.49 81.962c3.968 5.291 56.15 85.334 136 119.638 19.008 8.17 33.814 13.056 45.398 16.704 19.072 6.037 36.437 5.184 50.133 3.157 15.296-2.283 47.125-19.2 53.76-37.675 6.613-18.56 6.613-34.389 4.65-37.717-1.983-3.264-7.295-5.27-15.274-9.237-7.957-3.947-47.125-23.126-54.4-25.771-7.296-2.667-12.587-3.968-17.92 3.947-5.312 7.936-20.565 25.792-25.195 31.061-4.65 5.312-9.301 5.973-17.258 2.005-7.979-3.968-33.622-12.33-64-39.338-23.68-20.992-39.68-46.955-44.331-54.912-4.65-7.915-.47-12.203 3.52-16.15 3.563-3.541 7.936-9.258 11.904-13.866 3.99-4.651 5.333-7.958 7.979-13.227 2.645-5.29 1.322-9.92-.64-13.888-2.006-3.968-17.92-42.987-24.555-58.859h-.021z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-youtube"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#DD1829" />
<path
d="M800.305 372.2c-12.805-42.429-22.873-65.942-65.303-71.064 0 0-113.644-5.761-226.64-5.761-111.716 0-222.797 5.761-222.797 5.761-44.992 5.122-55.7 29.915-67.223 71.065 0 0-11.524 65.527-11.524 131.886 0 68.066 11.524 137.008 11.524 137.008 8.963 39.87 27.354 65.943 67.223 71.065 0 0 123.292 7.682 240.724 7.682 106.78 0 208.714-7.682 208.714-7.682 39.87-7.682 53.78-28.635 65.303-71.065 0 0 11.523-63.022 11.523-128.045 0-69.288-11.524-140.85-11.524-140.85zM448.82 619.97V393.33l174.781 113.32L448.82 619.97z"
fill="#FFF"
/>
</svg>
</template>
<template>
<svg
class="icon icon-zhihu"
viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="512" cy="512" r="512" fill="#006CE2" />
<path
d="M513.65 491.261H411.551c1.615-16.154 5.815-60.095 5.815-84.973 0-24.88-.323-60.742-.323-60.742h102.744V329.39c0-21.647-9.37-31.34-17.124-31.34h-178.67s5.169-17.77 10.015-36.186c4.846-18.417 15.832-44.264 15.832-44.264-63.003 4.2-67.958 50.941-81.743 92.729-13.787 41.785-24.556 62.356-44.586 107.912 27.786 0 55.249-13.57 66.879-32.309 11.631-18.74 16.908-40.71 16.908-40.71h62.035v59.019c0 21.107-3.878 87.45-3.878 87.45H254.742c-19.386 0-29.724 48.894-29.724 48.894h133.76c-8.4 75.82-26.493 106.191-51.91 152.716-25.418 46.525-92.728 99.406-92.728 99.406 41.033 11.63 86.589-3.555 105.974-21.972 19.386-18.417 35.863-49.756 47.817-72.838 11.954-23.081 21.972-65.124 21.972-65.124L498.462 766.86s4.846-24.233 6.461-39.418c1.616-15.186-.755-26.385-4.63-35.433-3.878-9.046-15.509-21.54-31.018-39.634-15.507-18.094-48.034-52.879-48.034-52.879s-15.832 11.63-28.108 21.001c9.046-21.97 16.262-79.695 16.262-79.695h122.343v-20.249c.003-17.66-7.319-29.29-18.089-29.29zm287.337-200.747h-234.35a4.308 4.308 0 0 0-4.309 4.308v435.099a4.308 4.308 0 0 0 4.308 4.308h40.226l14.7 50.402 81.096-50.402h98.328a4.308 4.308 0 0 0 4.308-4.308v-435.1a4.308 4.308 0 0 0-4.308-4.308zM755.97 684.47h-52.343l-61.548 39.095-10.823-39.095h-18.738V338.116H755.97v346.355z"
fill="#FFF"
/>
</svg>
</template>
import type { Context, PluginOptionAPI } from "@mr-hope/vuepress-types";
import type { HopeNavBarConfig, HopeSideBarConfig, HopeThemeConfig, ResolvedHopeThemeConfig } from "./types";
declare const themeAPI: {
(themeConfig: ResolvedHopeThemeConfig, ctx: Context): PluginOptionAPI;
config: (config: import("./types").HopeVuePressConfig) => import("./types").ResolvedHopeVuePressConfig;
themeConfig(themeConfig: HopeThemeConfig): HopeThemeConfig;
navbarConfig(navbarConfig: HopeNavBarConfig): HopeNavBarConfig;
sidebarConfig(sidebarConfig: HopeSideBarConfig): HopeSideBarConfig;
};
export = themeAPI;
"use strict";
const alias_1 = require("./node/alias");
const config_1 = require("./node/config");
const eject_1 = require("./node/eject");
const plugins_1 = require("./node/plugins");
const blogAddtionalPages = [
{
path: "/article/",
frontmatter: { layout: "Blog" },
},
{
path: "/star/",
frontmatter: { layout: "Blog" },
},
{
path: "/encrypt/",
frontmatter: { layout: "Blog" },
},
{
path: "/slide/",
frontmatter: { layout: "Blog" },
},
{
path: "/timeline/",
frontmatter: { layout: "Blog" },
},
];
// Theme API.
const themeAPI = (themeConfig, ctx) => ({
alias: (0, alias_1.getAlias)(themeConfig, ctx),
plugins: (0, plugins_1.getPluginConfig)(themeConfig),
additionalPages: themeConfig.blog === false ? [] : blogAddtionalPages,
extendCli: (cli) => {
cli
.command("eject-hope [targetDir]", "copy vuepress-theme-hope into .vuepress/theme for customization.")
.option("--debug", "eject in debug mode")
.action((dir) => {
void (0, eject_1.eject)(dir || ".");
});
},
});
themeAPI.config = config_1.config;
// helper functions
themeAPI.themeConfig = (themeConfig) => themeConfig;
themeAPI.navbarConfig = (navbarConfig) => navbarConfig;
themeAPI.sidebarConfig = (sidebarConfig) => sidebarConfig;
module.exports = themeAPI;
//# sourceMappingURL=index.js.map
\ No newline at end of file
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":";AACA,wCAAwC;AACxC,0CAAuC;AACvC,wCAAqC;AACrC,4CAAiD;AAUjD,MAAM,kBAAkB,GAAG;IACzB;QACE,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;KAChC;IACD;QACE,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;KAChC;IACD;QACE,IAAI,EAAE,WAAW;QACjB,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;KAChC;IACD;QACE,IAAI,EAAE,SAAS;QACf,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;KAChC;IACD;QACE,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;KAChC;CACF,CAAC;AAEF,aAAa;AACb,MAAM,QAAQ,GAAG,CACf,WAAoC,EACpC,GAAY,EACK,EAAE,CAAC,CAAC;IACrB,KAAK,EAAE,IAAA,gBAAQ,EAAC,WAAW,EAAE,GAAG,CAAC;IAEjC,OAAO,EAAE,IAAA,yBAAe,EAAC,WAAW,CAAC;IAErC,eAAe,EAAE,WAAW,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB;IAErE,SAAS,EAAE,CAAC,GAAQ,EAAQ,EAAE;QAC5B,GAAG;aACA,OAAO,CACN,wBAAwB,EACxB,kEAAkE,CACnE;aACA,MAAM,CAAC,SAAS,EAAE,qBAAqB,CAAC;aACxC,MAAM,CAAC,CAAC,GAAW,EAAE,EAAE;YACtB,KAAK,IAAA,aAAK,EAAC,GAAG,IAAI,GAAG,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACP,CAAC;CACF,CAAC,CAAC;AAEH,QAAQ,CAAC,MAAM,GAAG,eAAM,CAAC;AAEzB,mBAAmB;AACnB,QAAQ,CAAC,WAAW,GAAG,CAAC,WAA4B,EAAmB,EAAE,CACvE,WAAW,CAAC;AACd,QAAQ,CAAC,YAAY,GAAG,CAAC,YAA8B,EAAoB,EAAE,CAC3E,YAAY,CAAC;AACf,QAAQ,CAAC,aAAa,GAAG,CACvB,aAAgC,EACb,EAAE,CAAC,aAAa,CAAC;AAEtC,iBAAS,QAAQ,CAAC"}
\ No newline at end of file
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, {
back(): void;
}, {
i18n: {
hint: string[];
home: string;
back: string;
};
msg: string;
}, Record<never, any>>;
export default _default;
import Vue from "vue";
import Common from "@theme/components/Common.vue";
import Page404Icon from "@theme/icons/Page404Icon.vue";
import { getDefaultLocale } from "@mr-hope/vuepress-shared";
export default Vue.extend({
name: "NotFound",
components: {
Common,
Page404Icon,
},
computed: {
i18n() {
return this.$themeLocaleConfig.error404 || getDefaultLocale().error404;
},
msg() {
return this.i18n.hint[Math.floor(Math.random() * this.i18n.hint.length)];
},
},
methods: {
back() {
window.history.go(-1);
},
},
});
//# sourceMappingURL=404.js.map
\ No newline at end of file
{"version":3,"file":"404.js","sourceRoot":"","sources":["404.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,MAAM,MAAM,8BAA8B,CAAC;AAClD,OAAO,WAAW,MAAM,8BAA8B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAI5D,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,UAAU;IAEhB,UAAU,EAAE;QACV,MAAM;QACN,WAAW;KACZ;IAED,QAAQ,EAAE;QACR,IAAI;YACF,OAAO,IAAI,CAAC,kBAAkB,CAAC,QAAQ,IAAI,gBAAgB,EAAE,CAAC,QAAQ,CAAC;QACzE,CAAC;QAED,GAAG;YACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3E,CAAC;KACF;IAED,OAAO,EAAE;QACP,IAAI;YACF,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<Common :sidebar="false">
<main class="page not-found">
<Page404Icon />
<blockquote v-text="msg" />
<button class="action-button" @click="back">{{ i18n.back }}</button>
<RouterLink class="action-button" to="/">{{ i18n.home }}</RouterLink>
</main>
</Common>
</template>
<script src="./404" />
<style lang="stylus">
.page.not-found
display block
max-width $homePageWidth
margin 0px auto
padding ($navbarHeight + 1rem) 2rem 2rem 2rem !important
text-align center
.page-404-icon
width 50%
margin 0 auto
.action-button
display inline-block
box-sizing border-box
margin 0 0.25rem
padding 0.5rem 1rem
border-width 0
border-bottom 1px solid var(--accent-color-d10)
border-radius 0.25rem
background var(--accent-color)
color var(--white)
font-size 1rem
outline none
transition background 0.1s ease
&:hover
cursor pointer
background var(--accent-color-l10)
</style>
import BlogInfo from "@BlogInfo";
declare const _default: import("vue/types/vue").ExtendedVue<Record<never, any> & {
globalEncryptPassword: string;
} & {
checkGlobalPassword(globalPassword: string): void;
} & {
isGlobalEncrypted: boolean;
} & {
encryptOptions: import("../types").EncryptOptions;
} & BlogInfo, unknown, unknown, unknown, Record<never, any>>;
export default _default;
import BlogInfo from "@BlogInfo";
import BlogPage from "@BlogPage";
import Common from "@theme/components/Common.vue";
import MyTransition from "@theme/components/MyTransition.vue";
import { globalEncryptMixin } from "@theme/mixins/globalEncrypt";
import { pathEncryptMixin } from "@theme/mixins/pathEncrypt";
import Password from "@theme/components/Password.vue";
export default globalEncryptMixin.extend(pathEncryptMixin).extend({
components: {
BlogInfo,
BlogPage,
Common,
MyTransition,
Password,
},
});
//# sourceMappingURL=Blog.js.map
\ No newline at end of file
{"version":3,"file":"Blog.js","sourceRoot":"","sources":["Blog.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,8BAA8B,CAAC;AAClD,OAAO,YAAY,MAAM,oCAAoC,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,QAAQ,MAAM,gCAAgC,CAAC;AAEtD,eAAe,kBAAkB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC;IAChE,UAAU,EAAE;QACV,QAAQ;QACR,QAAQ;QACR,MAAM;QACN,YAAY;QACZ,QAAQ;KACT;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<Common :sidebar="false">
<template #sidebar-bottom>
<BlogInfo />
</template>
<Password v-if="isGlobalEncrypted" @password-verify="checkGlobalPassword" />
<Password
v-else-if="isPathEncrypted"
@password-verify="checkPathPassword"
/>
<main v-else class="page blog">
<div class="blog-page-wrapper">
<BlogPage />
<MyTransition :delay="0.16">
<BlogInfo />
</MyTransition>
</div>
</main>
</Common>
</template>
<script src="./Blog" />
<style lang="stylus">
.page.blog
box-sizing border-box
min-height 100vh
margin 0px auto
padding-top $navbarHeight
padding-bottom 2rem
background var(--bgcolor-light)
display flex
flex-direction column
justify-content space-between
@media (max-width $MQMobile)
padding $navbarMobileHeight 1.5rem 2rem
@media (max-width $MQMobileNarrow)
padding-left 0
padding-right 0
.blog-page-wrapper
box-sizing border-box
width 100%
margin 0 auto
display flex
justify-content center
align-items flex-start
@media (min-width $MQMobile)
padding 0 1rem
@media (min-width $MQNarrow)
padding 0 2rem
@media (min-width $MQWide)
padding 0
</style>
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, unknown, Record<never, any>>;
export default _default;
import Vue from "vue";
import BlogInfo from "@BlogInfo";
import BlogHome from "@BlogHome";
import ContentBottom from "@ContentBottom";
import ContentTop from "@ContentTop";
import NavbarStart from "@NavbarStart";
import NavbarCenter from "@NavbarCenter";
import NavbarEnd from "@NavbarEnd";
import PageBottom from "@PageBottom";
import PageTop from "@PageTop";
import SidebarBottom from "@SidebarBottom";
import SidebarCenter from "@SidebarCenter";
import SidebarTop from "@SidebarTop";
import Common from "@theme/components/Common.vue";
import Home from "@theme/components/Home.vue";
import Page from "@theme/components/Page.vue";
export default Vue.extend({
name: "Layout",
components: {
BlogInfo,
BlogHome,
Common,
ContentBottom,
ContentTop,
Home,
NavbarCenter,
NavbarEnd,
NavbarStart,
Page,
PageBottom,
PageTop,
SidebarBottom,
SidebarCenter,
SidebarTop,
},
});
//# sourceMappingURL=Layout.js.map
\ No newline at end of file
{"version":3,"file":"Layout.js","sourceRoot":"","sources":["Layout.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,aAAa,MAAM,gBAAgB,CAAC;AAC3C,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,OAAO,MAAM,UAAU,CAAC;AAC/B,OAAO,aAAa,MAAM,gBAAgB,CAAC;AAC3C,OAAO,aAAa,MAAM,gBAAgB,CAAC;AAC3C,OAAO,UAAU,MAAM,aAAa,CAAC;AACrC,OAAO,MAAM,MAAM,8BAA8B,CAAC;AAClD,OAAO,IAAI,MAAM,4BAA4B,CAAC;AAC9C,OAAO,IAAI,MAAM,4BAA4B,CAAC;AAE9C,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,QAAQ;IAEd,UAAU,EAAE;QACV,QAAQ;QACR,QAAQ;QACR,MAAM;QACN,aAAa;QACb,UAAU;QACV,IAAI;QACJ,YAAY;QACZ,SAAS;QACT,WAAW;QACX,IAAI;QACJ,UAAU;QACV,OAAO;QACP,aAAa;QACb,aAAa;QACb,UAAU;KACX;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<Common :sidebar="$frontmatter.blog !== true">
<template #navbar-start>
<slot name="navbar-start">
<NavbarStart />
<Content slot-key="navbar-start" />
</slot>
</template>
<template #navbar-center>
<slot name="navbar-center">
<NavbarCenter />
<Content slot-key="navbar-center" />
</slot>
</template>
<template #navbar-end>
<slot name="navbar-end">
<NavbarEnd />
<Content slot-key="navbar-end" />
</slot>
</template>
<template #sidebar-top>
<slot name="sidebar-top">
<SidebarTop />
<Content slot-key="sidebar-top" />
</slot>
</template>
<template #sidebar-center>
<slot name="sidebar-center">
<SidebarCenter />
<Content slot-key="sidebar-center" />
</slot>
</template>
<template #sidebar-bottom>
<slot name="sidebar-bottom">
<SidebarBottom />
<Content slot-key="sidebar-bottom" />
</slot>
</template>
<template #default="slotProps">
<BlogHome v-if="$frontmatter.blog && $themeConfig.blog !== false" />
<Home v-else-if="$frontmatter.home" />
<Page
v-else
:headers="slotProps.headers"
:sidebar-items="slotProps.sidebarItems"
>
<template #top>
<slot name="page-top">
<PageTop />
<Content slot-key="page-top" />
</slot>
</template>
<template #content-top>
<slot name="content-top">
<ContentTop />
<Content slot-key="content-top" />
</slot>
</template>
<template #content-bottom>
<slot name="content-bottom">
<ContentBottom />
<Content slot-key="content-bottom" />
</slot>
</template>
<template #bottom>
<slot name="page-bottom">
<PageBottom />
<Content slot-key="page-bottom" />
</slot>
</template>
</Page>
</template>
</Common>
</template>
<script src="./Layout" />
import Vue from "vue";
declare const _default: import("vue/types/vue").ExtendedVue<Vue, {
showMenu: boolean;
}, {
toggle(): void;
back(): void;
home(): void;
clickOutside(): void;
}, unknown, Record<never, any>>;
export default _default;
import Vue from "vue";
import ClickOutside from "@theme/utils/click-outside";
import ThemeColor from "@theme/components/Theme/ThemeColor.vue";
export default Vue.extend({
name: "Slide",
components: { ThemeColor },
directives: { "click-outside": ClickOutside },
data: () => ({
showMenu: false,
}),
// eslint-disable-next-line vue/no-deprecated-destroyed-lifecycle
destroyed() {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
document.querySelector("html").classList.remove("reveal-full-page");
document.body.classList.remove("reveal-viewport");
document.body.style.removeProperty("--slide-width");
document.body.style.removeProperty("--slide-height");
},
methods: {
toggle() {
this.showMenu = !this.showMenu;
},
back() {
window.history.go(-1);
this.showMenu = false;
},
home() {
void this.$router.push("/");
this.showMenu = false;
},
clickOutside() {
this.showMenu = false;
},
},
});
//# sourceMappingURL=Slide.js.map
\ No newline at end of file
{"version":3,"file":"Slide.js","sourceRoot":"","sources":["Slide.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,YAAY,MAAM,4BAA4B,CAAC;AACtD,OAAO,UAAU,MAAM,wCAAwC,CAAC;AAEhE,eAAe,GAAG,CAAC,MAAM,CAAC;IACxB,IAAI,EAAE,OAAO;IAEb,UAAU,EAAE,EAAE,UAAU,EAAE;IAE1B,UAAU,EAAE,EAAE,eAAe,EAAE,YAAY,EAAE;IAE7C,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,QAAQ,EAAE,KAAK;KAChB,CAAC;IAEF,iEAAiE;IACjE,SAAS;QACP,oEAAoE;QACpE,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAE,CAAC,SAAS,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;QACrE,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAClD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QACpD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;IACvD,CAAC;IAED,OAAO,EAAE;QACP,MAAM;YACJ,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC;QACjC,CAAC;QAED,IAAI;YACF,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACxB,CAAC;QAED,IAAI;YACF,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACxB,CAAC;QAED,YAAY;YACV,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACxB,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
<template>
<div class="presentation">
<ThemeColor v-show="false" />
<Content :key="$route.path" class="presentation-content" />
<div
v-click-outside="clickOutside"
class="menu"
:class="{ active: showMenu }"
>
<button class="menu-button" @click="toggle">
<span class="icon" />
</button>
<button class="back-button" @click="back">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path
d="M1014.749 449.156v125.688H260.626l345.64 345.64-89.239 89.237L19.307 512l497.72-497.721 89.238 89.238-345.64 345.64h754.124z"
/>
</svg>
</button>
<button class="home-button" @click="home">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path
d="M780.106 420.978L506.994 147.866 233.882 420.978h.045v455.11H780.06v-455.11h.046zm90.977 90.976V876.09a91.022 91.022 0 01-91.023 91.022H233.927a91.022 91.022 0 01-91.022-91.022V511.954l-67.22 67.175-64.307-64.307 431.309-431.31c35.498-35.498 93.115-35.498 128.614 0l431.309 431.31-64.307 64.307L871.083 512z"
/>
</svg>
</button>
</div>
</div>
</template>
<script src="./Slide" />
<style lang="stylus">
@require '~@mr-hope/vuepress-shared/styles/reset'
.presentation
.md-presentation
min-width 100vw
min-height 100vh
.menu-button, .back-button, .home-button
button()
box-sizing content-box
position fixed
bottom 2rem
width 1rem
height 1rem
padding 0.5rem
border-radius 50%
background #bbb
color var(--white)
outline none
&:hover
background var(--accent-color)
.theme-dark &
background #666
&:hover
background var(--accent-color)
.menu-button
left 2rem
transition transform 0.2s ease-in-out
vertical-align middle
z-index 50
&::before
content ' '
margin-top 0.125em
&::after
content ' '
margin-bottom 0.125em
.icon
margin 0.2em 0
&::before, &::after, .icon
display block
width 100%
height 0.2em
transition transform 0.2s ease-in-out
border-radius 0.05em
background var(--white)
.active .menu-button
&::before
transform translateY(0.4em) rotate(135deg)
.icon
transform scale(0)
&::after
transform translateY(-0.4em) rotate(-135deg)
.back-button
left 2rem
opacity 0
transition left 0.2s ease-out, opacity 0.2s ease-out
z-index 49
fill var(--white)
.active .back-button
left 4.5rem
opacity 1
.home-button
left 2rem
opacity 0
transition left 0.2s ease-out, opacity 0.2s ease-out
z-index 48
fill var(--white)
.active .home-button
left 7rem
opacity 1
</style>
import Vue from "vue";
import type { EncryptOptions } from "../types";
export declare const encryptBaseMixin: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
encryptOptions: EncryptOptions;
}, Record<never, any>>;
import Vue from "vue";
export const encryptBaseMixin = Vue.extend({
computed: {
encryptOptions() {
return this.$themeConfig.encrypt || {};
},
},
});
//# sourceMappingURL=encrypt.js.map
\ No newline at end of file
{"version":3,"file":"encrypt.js","sourceRoot":"","sources":["encrypt.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AAItB,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC;IACzC,QAAQ,EAAE;QACR,cAAc;YACZ,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,IAAI,EAAE,CAAC;QACzC,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
import type { EncryptOptions } from "../types";
export declare const globalEncryptMixin: import("vue/types/vue").ExtendedVue<{
encryptOptions: EncryptOptions;
} & Record<never, any> & import("vue").default, {
globalEncryptPassword: string;
}, {
checkGlobalPassword(globalPassword: string): void;
}, {
isGlobalEncrypted: boolean;
}, Record<never, any>>;
import { compareSync } from "bcryptjs";
import { encryptBaseMixin } from "@theme/mixins/encrypt";
export const globalEncryptMixin = encryptBaseMixin.extend({
data: () => ({
globalEncryptPassword: "",
}),
computed: {
isGlobalEncrypted() {
if (this.encryptOptions.status === "global" &&
this.encryptOptions.global) {
const { global } = this.encryptOptions;
const globalPasswords = typeof global === "string" ? [global] : global;
// none of the password matches
return !globalPasswords.some((globalPassword) => compareSync(this.globalEncryptPassword, globalPassword));
}
return false;
},
},
mounted() {
const globalPassword = localStorage.getItem("globalPassword");
if (globalPassword)
this.globalEncryptPassword = globalPassword;
},
methods: {
checkGlobalPassword(globalPassword) {
const { global } = this.encryptOptions;
const globalPasswords = typeof global === "string" ? [global] : global;
if (
// some of the password matches
globalPasswords.some((password) => compareSync(globalPassword, password))) {
this.globalEncryptPassword = globalPassword;
localStorage.setItem("globalPassword", globalPassword);
}
},
},
});
//# sourceMappingURL=globalEncrypt.js.map
\ No newline at end of file
{"version":3,"file":"globalEncrypt.js","sourceRoot":"","sources":["globalEncrypt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAIzD,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC,MAAM,CAAC;IACxD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,qBAAqB,EAAE,EAAE;KAC1B,CAAC;IAEF,QAAQ,EAAE;QACR,iBAAiB;YACf,IACE,IAAI,CAAC,cAAc,CAAC,MAAM,KAAK,QAAQ;gBACvC,IAAI,CAAC,cAAc,CAAC,MAAM,EAC1B;gBACA,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,cAAc,CAAC;gBACvC,MAAM,eAAe,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;gBAEvE,+BAA+B;gBAC/B,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE,EAAE,CAC9C,WAAW,CAAC,IAAI,CAAC,qBAAqB,EAAE,cAAc,CAAC,CACxD,CAAC;aACH;YAED,OAAO,KAAK,CAAC;QACf,CAAC;KACF;IAED,OAAO;QACL,MAAM,cAAc,GAAG,YAAY,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAE9D,IAAI,cAAc;YAAE,IAAI,CAAC,qBAAqB,GAAG,cAAc,CAAC;IAClE,CAAC;IAED,OAAO,EAAE;QACP,mBAAmB,CAAC,cAAsB;YACxC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,cAA0C,CAAC;YACnE,MAAM,eAAe,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YAEvE;YACE,+BAA+B;YAC/B,eAAe,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAChC,WAAW,CAAC,cAAc,EAAE,QAAQ,CAAC,CACtC,EACD;gBACA,IAAI,CAAC,qBAAqB,GAAG,cAAc,CAAC;gBAC5C,YAAY,CAAC,OAAO,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC;aACxD;QACH,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
import type { EncryptOptions } from "../types";
export declare const pathEncryptMixin: import("vue/types/vue").ExtendedVue<{
encryptOptions: EncryptOptions;
} & Record<never, any> & import("vue").default, {
encryptPasswordConfig: Record<string, string>;
}, {
checkPathPassword(password: string): void;
}, {
pathEncryptMatchKeys: string[];
isPathEncrypted: boolean;
}, Record<never, any>>;
import { compareSync } from "bcryptjs";
import { encryptBaseMixin } from "@theme/mixins/encrypt";
import { getPathMatchedKeys } from "@theme/utils/encrypt";
export const pathEncryptMixin = encryptBaseMixin.extend({
data: () => ({
encryptPasswordConfig: {},
}),
computed: {
pathEncryptMatchKeys() {
return getPathMatchedKeys(this.encryptOptions, this.$route.path);
},
isPathEncrypted() {
if (this.pathEncryptMatchKeys.length === 0)
return false;
const { config } = this.encryptOptions;
// none of the password matches
return this.pathEncryptMatchKeys.every((key) => {
const keyConfig = config[key];
const hitPasswords = typeof keyConfig === "string" ? [keyConfig] : keyConfig;
return (!this.encryptPasswordConfig[key] ||
hitPasswords.every((encryptPassword) => !compareSync(this.encryptPasswordConfig[key], encryptPassword)));
});
},
},
mounted() {
const passwordConfig = localStorage.getItem("encryptConfig");
if (passwordConfig)
this.encryptPasswordConfig = JSON.parse(passwordConfig);
},
methods: {
checkPathPassword(password) {
const { config } = this.$themeConfig.encrypt;
for (const hitKey of this.pathEncryptMatchKeys) {
const hitPassword = config[hitKey];
const hitPasswordList = typeof hitPassword === "string" ? [hitPassword] : hitPassword;
// some of the password matches
if (hitPasswordList.filter((encryptPassword) => compareSync(password, encryptPassword))) {
this.$set(this.encryptPasswordConfig, hitKey, password);
localStorage.setItem("encryptConfig", JSON.stringify(this.encryptPasswordConfig));
break;
}
}
},
},
});
//# sourceMappingURL=pathEncrypt.js.map
\ No newline at end of file
{"version":3,"file":"pathEncrypt.js","sourceRoot":"","sources":["pathEncrypt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAI1D,MAAM,CAAC,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,MAAM,CAAC;IACtD,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;QACX,qBAAqB,EAAE,EAA4B;KACpD,CAAC;IAEF,QAAQ,EAAE;QACR,oBAAoB;YAClB,OAAO,kBAAkB,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC;QAED,eAAe;YACb,IAAI,IAAI,CAAC,oBAAoB,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;YAEzD,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,cAA0C,CAAC;YAEnE,+BAA+B;YAC/B,OAAO,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC7C,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC9B,MAAM,YAAY,GAChB,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAE1D,OAAO,CACL,CAAC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC;oBAChC,YAAY,CAAC,KAAK,CAChB,CAAC,eAAe,EAAE,EAAE,CAClB,CAAC,WAAW,CAAC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC,CACjE,CACF,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;KACF;IAED,OAAO;QACL,MAAM,cAAc,GAAG,YAAY,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;QAE7D,IAAI,cAAc;YAChB,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAGrD,CAAC;IACN,CAAC;IAED,OAAO,EAAE;QACP,iBAAiB,CAAC,QAAgB;YAChC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,CAAC,OAAmC,CAAC;YAEzE,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,oBAAoB,EAAE;gBAC9C,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;gBACnC,MAAM,eAAe,GACnB,OAAO,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;gBAEhE,+BAA+B;gBAC/B,IACE,eAAe,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,EAAE,CACzC,WAAW,CAAC,QAAQ,EAAE,eAAe,CAAC,CACvC,EACD;oBACA,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;oBACxD,YAAY,CAAC,OAAO,CAClB,eAAe,EACf,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAC3C,CAAC;oBAEF,MAAM;iBACP;aACF;QACH,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
import Vue from "vue";
import type { PageComputed } from "@mr-hope/vuepress-types";
export declare const starMixin: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
$starArticles: PageComputed[];
}, Record<never, any>>;
import Vue from "vue";
import { filterArticle, sortArticle } from "@theme/utils/article";
export const starMixin = Vue.extend({
computed: {
$starArticles() {
const { pages } = this.$site;
// filter before sort
return sortArticle(filterArticle(pages, (page) => Boolean(page.frontmatter.star)), "star");
},
},
});
//# sourceMappingURL=star.js.map
\ No newline at end of file
{"version":3,"file":"star.js","sourceRoot":"","sources":["star.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAIlE,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC;IAClC,QAAQ,EAAE;QACR,aAAa;YACX,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAE7B,qBAAqB;YACrB,OAAO,WAAW,CAChB,aAAa,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,EAC9D,MAAM,CACP,CAAC;QACJ,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
import Vue from "vue";
import type { PageComputed } from "@mr-hope/vuepress-types";
export interface TimelineItem {
year: number;
articles: PageComputed[];
}
export declare const timelineMixin: import("vue/types/vue").ExtendedVue<Vue, unknown, unknown, {
$timelineItems: PageComputed[];
$timeline: TimelineItem[];
}, Record<never, any>>;
import Vue from "vue";
import { filterArticle, getDate, sortArticle } from "@theme/utils/article";
export const timelineMixin = Vue.extend({
computed: {
$timelineItems() {
const { pages } = this.$site;
// filter before sort
return sortArticle(filterArticle(pages, (page) => Boolean(page.frontmatter.time ||
page.frontmatter.date ||
page.createTimeStamp) && page.frontmatter.timeline !== false));
},
/** Timeline list */
$timeline() {
const timelineItems = [];
// filter before sort
this.$timelineItems.forEach((article) => {
const { frontmatter: { date, time = date }, createTimeStamp, } = article;
const [year, month, day] = getDate((time || createTimeStamp));
if (year && month && day) {
if (!timelineItems[0] || timelineItems[0].year !== year)
timelineItems.unshift({ year, articles: [] });
article.frontmatter.parsedDate = `${month}/${day}`;
timelineItems[0].articles.push(article);
}
});
return timelineItems.reverse();
},
},
});
//# sourceMappingURL=timeline.js.map
\ No newline at end of file
{"version":3,"file":"timeline.js","sourceRoot":"","sources":["timeline.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAS3E,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,CAAC;IACtC,QAAQ,EAAE;QACR,cAAc;YACZ,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;YAE7B,qBAAqB;YACrB,OAAO,WAAW,CAChB,aAAa,CACX,KAAK,EACL,CAAC,IAAI,EAAE,EAAE,CACP,OAAO,CACL,IAAI,CAAC,WAAW,CAAC,IAAI;gBACnB,IAAI,CAAC,WAAW,CAAC,IAAI;gBACrB,IAAI,CAAC,eAAe,CACvB,IAAI,IAAI,CAAC,WAAW,CAAC,QAAQ,KAAK,KAAK,CAC3C,CACF,CAAC;QACJ,CAAC;QAED,oBAAoB;QACpB,SAAS;YACP,MAAM,aAAa,GAAmB,EAAE,CAAC;YAEzC,qBAAqB;YACrB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBACtC,MAAM,EACJ,WAAW,EAAE,EAAE,IAAI,EAAE,IAAI,GAAG,IAAI,EAAE,EAClC,eAAe,GAChB,GAAG,OAAO,CAAC;gBACZ,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,GAAG,OAAO,CAChC,CAAC,IAAI,IAAI,eAAe,CAA2B,CACpD,CAAC;gBAEF,IAAI,IAAI,IAAI,KAAK,IAAI,GAAG,EAAE;oBACxB,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI;wBACrD,aAAa,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;oBAEhD,OAAO,CAAC,WAAW,CAAC,UAAU,GAAG,GAAG,KAAK,IAAI,GAAG,EAAE,CAAC;oBACnD,aAAa,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;iBACzC;YACH,CAAC,CAAC,CAAC;YAEH,OAAO,aAAa,CAAC,OAAO,EAAE,CAAC;QACjC,CAAC;KACF;CACF,CAAC,CAAC"}
\ No newline at end of file
import type { Context } from "@mr-hope/vuepress-types";
import type { ResolvedHopeThemeConfig } from "../types";
export declare const getAlias: (themeConfig: ResolvedHopeThemeConfig, ctx: Context) => Record<string, string>;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAlias = void 0;
const path_1 = require("path");
const getAlias = (themeConfig, ctx) => {
const { siteConfig } = ctx;
// Resolve algolia
const isAlgoliaSearch = Boolean(themeConfig.algolia) ||
Object.keys((siteConfig.locales && themeConfig.locales) || {}).some((base) => themeConfig.locales[base].algolia);
const blogEnabled = themeConfig.blog !== false;
const commentEnabled = themeConfig.comment &&
themeConfig.comment.type &&
themeConfig.comment.type !== "disable";
const themeColorEnabled = !(themeConfig.themeColor === false && themeConfig.darkmode === "disable");
const { custom = {} } = themeConfig;
const noopModule = "@mr-hope/vuepress-shared/lib/esm/noopModule";
return {
"@AlgoliaSearchBox": isAlgoliaSearch
? themeConfig.algoliaType === "full"
? (0, path_1.resolve)(__dirname, "../components/AlgoliaSearch/Full.vue")
: (0, path_1.resolve)(__dirname, "../components/AlgoliaSearch/Dropdown.vue")
: noopModule,
"@BlogInfo": blogEnabled
? (0, path_1.resolve)(__dirname, "../components/Blog/BlogInfo.vue")
: noopModule,
"@BloggerInfo": blogEnabled
? (0, path_1.resolve)(__dirname, "../components/Blog/BloggerInfo.vue")
: noopModule,
"@BlogHome": blogEnabled
? (0, path_1.resolve)(__dirname, "../components/Blog/BlogHome.vue")
: noopModule,
"@BlogPage": blogEnabled
? (0, path_1.resolve)(__dirname, "../components/Blog/BlogPage.vue")
: noopModule,
"@ContentTop": custom.contentTop
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.contentTop)
: noopModule,
"@ContentBottom": custom.contentBottom
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.contentBottom)
: noopModule,
"@PageTop": custom.pageTop
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.pageTop)
: noopModule,
"@PageBottom": custom.pageBottom
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.pageBottom)
: noopModule,
"@Comment": commentEnabled
? "@mr-hope/vuepress-plugin-comment/lib/client/Comment.vue"
: noopModule,
"@NavbarStart": custom.navbarStart
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.navbarStart)
: noopModule,
"@NavbarCenter": custom.navbarCenter
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.navbarCenter)
: noopModule,
"@NavbarEnd": custom.navbarEnd
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.navbarEnd)
: noopModule,
"@ThemeColor": themeColorEnabled
? (0, path_1.resolve)(__dirname, "../components/Theme/ThemeColor.vue")
: noopModule,
"@SidebarTop": custom.sidebarTop
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.sidebarTop)
: noopModule,
"@SidebarCenter": custom.sidebarCenter
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.sidebarCenter)
: noopModule,
"@SidebarBottom": custom.sidebarBottom
? (0, path_1.resolve)(ctx.sourceDir, ".vuepress", custom.sidebarBottom)
: noopModule,
};
};
exports.getAlias = getAlias;
//# sourceMappingURL=alias.js.map
\ No newline at end of file
{"version":3,"file":"alias.js","sourceRoot":"","sources":["alias.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
import type { Plugin } from "@mr-hope/vuepress-types";
import type { ChunkRenameOptions } from "../types";
export declare const chunkRenamePlugin: Plugin<ChunkRenameOptions>;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.chunkRenamePlugin = void 0;
const chunkRenamePlugin = ({ pageChunkName = ({ title = "", key }) => {
const chunkTitle = (title || "").replace(/[.&*?#\\/:"<>| ]/gu, "");
return chunkTitle ? `page-${chunkTitle}` : `page-${key.slice(1)}`;
}, layoutChunkName = (layout) => `layout-${layout.componentName}`, }, context) => {
// override internal plugins
const plugins = [];
if (pageChunkName) {
plugins.push({
name: "@vuepress/internal-page-components",
extendPageData(page) {
page._chunkName = pageChunkName(page);
},
clientDynamicModules() {
const content = `export default {\n${context.pages
.filter(({ _filePath }) => _filePath)
.map((page) => {
const key = JSON.stringify(page.key);
const filePath = JSON.stringify(page._filePath);
const comment = page._chunkName
? `/* webpackChunkName: ${JSON.stringify(page._chunkName)} */`
: "";
return ` ${key}: () => import(${comment}${filePath})`;
})
.join(",\n")} \n}`;
return {
dirname: "internal",
name: "page-components.js",
content,
};
},
});
}
if (layoutChunkName) {
const { layoutComponentMap } = context.themeAPI;
for (const key in layoutComponentMap) {
const component = layoutComponentMap[key];
component._chunkName = layoutChunkName(component);
}
plugins.push({
name: "@vuepress/internal-layout-components",
clientDynamicModules() {
const { layoutComponentMap } = context.themeAPI;
const content = `export default {\n${Object.keys(layoutComponentMap)
.map((name) => {
const component = layoutComponentMap[name];
const key = JSON.stringify(name);
const filePath = JSON.stringify(component.path);
const comment = component._chunkName
? `/* webpackChunkName: ${JSON.stringify(component._chunkName)} */`
: "";
return ` ${key}: () => import(${comment}${filePath})`;
})
.join(",\n")} \n}`;
return {
dirname: "internal",
name: "layout-components.js",
content,
};
},
});
}
return {
name: "chunk-rename",
plugins,
};
};
exports.chunkRenamePlugin = chunkRenamePlugin;
//# sourceMappingURL=chunk-rename.js.map
\ No newline at end of file
{"version":3,"file":"chunk-rename.js","sourceRoot":"","sources":["chunk-rename.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
import type { Plugin } from "@mr-hope/vuepress-types";
import type { CleanUrlOptions } from "../types";
export declare const cleanUrlPlugin: Plugin<CleanUrlOptions>;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cleanUrlPlugin = void 0;
const cleanUrlPlugin = ({ normalSuffix = "", indexSuffix = "/", notFoundPath = "/404.html", }) => ({
name: "clean-url",
extendPageData(page) {
const { regularPath, frontmatter = {} } = page;
if (!frontmatter.permalink) {
if (regularPath === "/404.html")
// path for 404 page
page.path = notFoundPath;
else if (regularPath.endsWith(".html"))
// normal path
// e.g. foo/bar.md -> foo/bar.html
page.path = `${regularPath.slice(0, -5)}${normalSuffix}`;
else if (regularPath.endsWith("/"))
// index path
// e.g. foo/index.md -> foo/
page.path = `${regularPath.slice(0, -1)}${indexSuffix}`;
}
},
});
exports.cleanUrlPlugin = cleanUrlPlugin;
//# sourceMappingURL=clean-url.js.map
\ No newline at end of file
{"version":3,"file":"clean-url.js","sourceRoot":"","sources":["clean-url.ts"],"names":[],"mappings":";;;AAGO,MAAM,cAAc,GAA4B,CAAC,EACtD,YAAY,GAAG,EAAE,EACjB,WAAW,GAAG,GAAG,EACjB,YAAY,GAAG,WAAW,GAC3B,EAAE,EAAE,CAAC,CAAC;IACL,IAAI,EAAE,WAAW;IAEjB,cAAc,CAAC,IAAI;QACjB,MAAM,EAAE,WAAW,EAAE,WAAW,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC;QAE/C,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE;YAC1B,IAAI,WAAW,KAAK,WAAW;gBAC7B,oBAAoB;gBACpB,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;iBACtB,IAAI,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC;gBACpC,cAAc;gBACd,kCAAkC;gBAClC,IAAI,CAAC,IAAI,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,EAAE,CAAC;iBACtD,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAChC,aAAa;gBACb,4BAA4B;gBAC5B,IAAI,CAAC,IAAI,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,WAAW,EAAE,CAAC;SAC3D;IACH,CAAC;CACF,CAAC,CAAC;AAxBU,QAAA,cAAc,kBAwBxB"}
\ No newline at end of file
import type { HopeVuePressConfig, ResolvedHopeVuePressConfig } from "../types";
export declare const config: (config: HopeVuePressConfig) => ResolvedHopeVuePressConfig;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.config = void 0;
const vuepress_shared_1 = require("@mr-hope/vuepress-shared");
const locales_1 = require("./locales");
const themeConfig_1 = require("./themeConfig");
const defaultConfig = {
base: process.env.VuePress_BASE || "/",
temp: "./node_modules/.temp",
theme: "hope",
themeConfig: { locales: {} },
evergreen: true,
};
const getRootLang = (config) => {
var _a, _b;
// infer from siteLocale
const siteLocales = config.locales;
if ((siteLocales === null || siteLocales === void 0 ? void 0 : siteLocales["/"]) && (0, vuepress_shared_1.checkLang)((_a = siteLocales["/"]) === null || _a === void 0 ? void 0 : _a.lang))
return siteLocales["/"].lang;
// infer from themeLocale
const themeLocales = config.locales;
if ((themeLocales === null || themeLocales === void 0 ? void 0 : themeLocales["/"]) && (0, vuepress_shared_1.checkLang)((_b = themeLocales["/"]) === null || _b === void 0 ? void 0 : _b.lang))
return themeLocales["/"].lang;
(0, vuepress_shared_1.showLangError)("root");
return "en-US";
};
const config = (config) => {
// merge default config
(0, vuepress_shared_1.deepAssignReverse)(defaultConfig, config);
const resolvedConfig = config;
const rootLang = getRootLang(resolvedConfig);
(0, themeConfig_1.resolveThemeConfig)(resolvedConfig.themeConfig, rootLang);
(0, locales_1.resolveLocales)(resolvedConfig, rootLang);
return resolvedConfig;
};
exports.config = config;
//# sourceMappingURL=config.js.map
\ No newline at end of file
{"version":3,"file":"config.js","sourceRoot":"","sources":["config.ts"],"names":[],"mappings":";;;AAAA,8DAIkC;AAClC,uCAA2C;AAC3C,+CAAmD;AAKnD,MAAM,aAAa,GAAG;IACpB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,GAAG;IAEtC,IAAI,EAAE,sBAAsB;IAE5B,KAAK,EAAE,MAAM;IAEb,WAAW,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;IAE5B,SAAS,EAAE,IAAI;CAChB,CAAC;AAEF,MAAM,WAAW,GAAG,CAAC,MAAkC,EAAY,EAAE;;IACnE,wBAAwB;IACxB,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,CAAC;IAEnC,IAAI,CAAA,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAG,GAAG,CAAC,KAAI,IAAA,2BAAS,EAAC,MAAA,WAAW,CAAC,GAAG,CAAC,0CAAE,IAAI,CAAC;QACzD,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,IAAgB,CAAC;IAE3C,yBAAyB;IACzB,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC;IAEpC,IAAI,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAG,GAAG,CAAC,KAAI,IAAA,2BAAS,EAAC,MAAA,YAAY,CAAC,GAAG,CAAC,0CAAE,IAAI,CAAC;QAC3D,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,IAAgB,CAAC;IAE5C,IAAA,+BAAa,EAAC,MAAM,CAAC,CAAC;IAEtB,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEK,MAAM,MAAM,GAAG,CACpB,MAA0B,EACE,EAAE;IAC9B,uBAAuB;IACvB,IAAA,mCAAiB,EAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IAEzC,MAAM,cAAc,GAAG,MAAoC,CAAC;IAC5D,MAAM,QAAQ,GAAG,WAAW,CAAC,cAAc,CAAC,CAAC;IAE7C,IAAA,gCAAkB,EAAC,cAAc,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAA,wBAAc,EAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IAEzC,OAAO,cAAc,CAAC;AACxB,CAAC,CAAC;AAbW,QAAA,MAAM,UAajB"}
\ No newline at end of file
export declare const eject: (dir: string) => Promise<void>;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.eject = void 0;
const chalk_1 = require("chalk");
const fs_extra_1 = require("fs-extra");
const path_1 = require("path");
// #region exclude-files
const EXCLUDED_FILES = [
"__tests__",
".npmignore",
"LICENSE",
"package.json",
"node_modules",
"README.md",
"readme.md",
];
// #endregion exclude-files
const eject = async (dir) => {
try {
const sourceDir = (0, path_1.resolve)(__dirname, "../");
const targetDir = (0, path_1.resolve)(process.cwd(), dir, ".vuepress/theme");
await (0, fs_extra_1.copy)(sourceDir, targetDir, {
filter: (src) => {
return !EXCLUDED_FILES.includes((0, path_1.relative)(sourceDir, src));
},
});
console.log(`Copied vuepress-theme-hope into ${(0, chalk_1.cyan)(targetDir)}.\n`);
}
catch (err) {
console.error((0, chalk_1.red)(err.stack || ""));
process.exitCode = 1;
}
};
exports.eject = eject;
//# sourceMappingURL=eject.js.map
\ No newline at end of file
{"version":3,"file":"eject.js","sourceRoot":"","sources":["eject.ts"],"names":[],"mappings":";;;AAAA,iCAAkC;AAClC,uCAAgC;AAChC,+BAAyC;AAEzC,wBAAwB;AACxB,MAAM,cAAc,GAAG;IACrB,WAAW;IACX,YAAY;IACZ,SAAS;IACT,cAAc;IACd,cAAc;IACd,WAAW;IACX,WAAW;CACZ,CAAC;AACF,2BAA2B;AAEpB,MAAM,KAAK,GAAG,KAAK,EAAE,GAAW,EAAiB,EAAE;IACxD,IAAI;QACF,MAAM,SAAS,GAAG,IAAA,cAAO,EAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,IAAA,cAAO,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC;QAEjE,MAAM,IAAA,eAAI,EAAC,SAAS,EAAE,SAAS,EAAE;YAC/B,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE;gBACd,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAA,eAAQ,EAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;YAC5D,CAAC;SACF,CAAC,CAAC;QAEH,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAA,YAAI,EAAC,SAAS,CAAC,KAAK,CAAC,CAAC;KACtE;IAAC,OAAO,GAAG,EAAE;QACZ,OAAO,CAAC,KAAK,CAAC,IAAA,WAAG,EAAE,GAAa,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;KACtB;AACH,CAAC,CAAC;AAhBW,QAAA,KAAK,SAgBhB"}
\ No newline at end of file
import type { EncryptOptions } from "../types";
export declare const resolveEncrypt: (encrypt: EncryptOptions) => void;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveEncrypt = void 0;
const bcryptjs_1 = require("bcryptjs");
const resolveEncrypt = (encrypt) => {
// handle global password
if (encrypt.global)
if (typeof encrypt.global === "string")
encrypt.global = (0, bcryptjs_1.hashSync)(encrypt.global, 10);
else if (Array.isArray(encrypt.global))
encrypt.global = encrypt.global.map((globalPassword) => {
if (typeof globalPassword === "string")
return (0, bcryptjs_1.hashSync)(globalPassword, 10);
throw new Error(`[vuepress-theme-hope]: You config "themeConfig.encrypt.global", but your config is invalid.
All password MUST be string. But we found one’s type is ${typeof globalPassword}. Please fix it!`);
});
else
throw new Error(`[vuepress-theme-hope]: You are asking for global encryption but you provide invalid "global" config.
Please check "global" in your "themeConfig.encrypt" config. It can be string or string[], but you are providing ${typeof encrypt.global}. Please fix it!`);
const passwordConfig = encrypt.config || {};
Object.keys(passwordConfig).forEach((key) => {
const password = passwordConfig[key];
if (typeof password === "string")
passwordConfig[key] = (0, bcryptjs_1.hashSync)(password, 10);
else if (Array.isArray(password))
passwordConfig[key] = password.map((configPassword) => {
if (typeof configPassword === "string")
return (0, bcryptjs_1.hashSync)(configPassword, 10);
throw new Error(`[vuepress-theme-hope]: You config "themeConfig.encrypt.config", but your config is invalid.
Key ${key}’s value MUST be string or string[]. But it’s type is ${typeof configPassword}. Please fix it!`);
});
else
throw new Error(`[vuepress-theme-hope]: You config "themeConfig.encrypt.config", but your config is invalid.
The value of key ${key} MUST be string or string[]. But not it’s ${typeof password}. Please fix it!`);
});
};
exports.resolveEncrypt = resolveEncrypt;
//# sourceMappingURL=encrypt.js.map
\ No newline at end of file
{"version":3,"file":"encrypt.js","sourceRoot":"","sources":["encrypt.ts"],"names":[],"mappings":";;;AAAA,uCAAoC;AAI7B,MAAM,cAAc,GAAG,CAAC,OAAuB,EAAQ,EAAE;IAC9D,yBAAyB;IACzB,IAAI,OAAO,CAAC,MAAM;QAChB,IAAI,OAAO,OAAO,CAAC,MAAM,KAAK,QAAQ;YACpC,OAAO,CAAC,MAAM,GAAG,IAAA,mBAAQ,EAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;aAC3C,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC;YACpC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,EAAE;gBACrD,IAAI,OAAO,cAAc,KAAK,QAAQ;oBACpC,OAAO,IAAA,mBAAQ,EAAC,cAAc,EAAE,EAAE,CAAC,CAAC;gBAEtC,MAAM,IAAI,KAAK,CACb;;oEAE0D,OAAO,cAAc,kBAAkB,CAClG,CAAC;YACJ,CAAC,CAAC,CAAC;;YAEH,MAAM,IAAI,KAAK,CACb;;kHAE0G,OAAO,OAAO,CAAC,MAAM,kBAAkB,CAClJ,CAAC;IAEN,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC;IAE5C,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;QAC1C,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QAErC,IAAI,OAAO,QAAQ,KAAK,QAAQ;YAC9B,cAAc,CAAC,GAAG,CAAC,GAAG,IAAA,mBAAQ,EAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;aAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;YAC9B,cAAc,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,EAAE;gBACpD,IAAI,OAAO,cAAc,KAAK,QAAQ;oBACpC,OAAO,IAAA,mBAAQ,EAAC,cAAc,EAAE,EAAE,CAAC,CAAC;gBAEtC,MAAM,IAAI,KAAK,CAAC;;MAElB,GAAG,yDAAyD,OAAO,cAAc,kBAAkB,CAAC,CAAC;YACrG,CAAC,CAAC,CAAC;;YAEH,MAAM,IAAI,KAAK,CACb;;mBAEW,GAAG,6CAA6C,OAAO,QAAQ,kBAAkB,CAC7F,CAAC;IACN,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AA9CW,QAAA,cAAc,kBA8CzB"}
\ No newline at end of file
import type { HopeLang } from "@mr-hope/vuepress-shared";
import type { ResolvedHopeVuePressConfig } from "../types";
export declare const resolveLocales: (config: ResolvedHopeVuePressConfig, rootLang: HopeLang) => void;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveLocales = void 0;
const vuepress_shared_1 = require("@mr-hope/vuepress-shared");
const resolveLocales = (config, rootLang) => {
// ensure locales config
if (!config.locales)
config.locales = {};
const { locales } = config;
// set locate for base
locales["/"] = Object.assign({ lang: rootLang }, (locales["/"] || {}));
// handle other languages
Object.keys(config.themeConfig.locales).forEach((path) => {
if (path === "/")
return;
locales[path] = Object.assign({ lang: (0, vuepress_shared_1.path2Lang)(path) }, (locales[path] || {}));
});
};
exports.resolveLocales = resolveLocales;
//# sourceMappingURL=locales.js.map
\ No newline at end of file
{"version":3,"file":"locales.js","sourceRoot":"","sources":["locales.ts"],"names":[],"mappings":";;;AAAA,8DAAqD;AAK9C,MAAM,cAAc,GAAG,CAC5B,MAAkC,EAClC,QAAkB,EACZ,EAAE;IACR,wBAAwB;IACxB,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC;IAEzC,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAE3B,sBAAsB;IACtB,OAAO,CAAC,GAAG,CAAC,mBACV,IAAI,EAAE,QAAQ,IACX,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CACxB,CAAC;IAEF,yBAAyB;IACzB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACvD,IAAI,IAAI,KAAK,GAAG;YAAE,OAAO;QAEzB,OAAO,CAAC,IAAI,CAAC,mBAAK,IAAI,EAAE,IAAA,2BAAS,EAAC,IAAI,CAAC,IAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAE,CAAC;IACtE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AArBW,QAAA,cAAc,kBAqBzB"}
\ No newline at end of file
import type { PluginConfig } from "@mr-hope/vuepress-types";
import type { ResolvedHopeThemeConfig } from "../types";
export declare const getPluginConfig: (themeConfig: ResolvedHopeThemeConfig) => PluginConfig[];
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getPluginConfig = void 0;
const path_1 = require("path");
const clean_url_1 = require("./clean-url");
const chunk_rename_1 = require("./chunk-rename");
const getPluginConfig = (themeConfig) => {
// set author for comment plugin
if (themeConfig.comment && themeConfig.author)
themeConfig.comment.author = themeConfig.author;
return [
["@mr-hope/comment", themeConfig.comment || true],
["@mr-hope/components"],
["@mr-hope/feed", themeConfig.feed],
["@mr-hope/git", themeConfig.git],
["@mr-hope/pwa", themeConfig.pwa],
["@mr-hope/seo", themeConfig.seo],
["@mr-hope/sitemap", themeConfig.sitemap],
[
"@mr-hope/smooth-scroll",
themeConfig.smoothScroll === false
? false
: typeof themeConfig.smoothScroll === "number"
? { delay: themeConfig.smoothScroll }
: themeConfig.smoothScroll || { delay: 500 },
],
[
"@vuepress/blog",
themeConfig.blog === false
? false
: {
frontmatters: [
{
id: "tag",
keys: ["tag", "tags"],
path: "/tag/",
layout: "Blog",
scopeLayout: "Blog",
},
{
id: "category",
keys: ["category", "categories"],
path: "/category/",
layout: "Blog",
scopeLayout: "Blog",
},
],
},
],
["@vuepress/last-updated", false],
"@vuepress/nprogress",
[
"@vuepress/search",
{
searchMaxSuggestions: themeConfig.searchMaxSuggestions || 10,
},
],
["active-hash", themeConfig.activeHash],
["add-this", typeof themeConfig.addThis === "string"],
[
"copyright",
typeof themeConfig.copyright === "object"
? Object.assign({ minLength: 100, disable: themeConfig.copyright.status === "local", clipboardComponent: (0, path_1.resolve)(__dirname, "../components/Clipboard.vue") }, themeConfig.copyright) : false,
],
["md-enhance", themeConfig.mdEnhance || {}],
["@mr-hope/copy-code", themeConfig.copyCode],
["photo-swipe", themeConfig.photoSwipe],
[
"typescript",
themeConfig.typescript
? {
tsLoaderOptions: typeof themeConfig.typescript === "object"
? themeConfig.typescript
: {},
}
: false,
],
[
clean_url_1.cleanUrlPlugin,
themeConfig.cleanUrl === false
? false
: themeConfig.cleanUrl || { normalSuffix: "/" },
],
[
chunk_rename_1.chunkRenamePlugin,
themeConfig.chunkRename === false ? false : themeConfig.chunkRename,
],
];
};
exports.getPluginConfig = getPluginConfig;
//# sourceMappingURL=plugins.js.map
\ No newline at end of file
{"version":3,"file":"plugins.js","sourceRoot":"","sources":["plugins.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
import type { HopeLang } from "@mr-hope/vuepress-shared";
import type { ResolvedHopeThemeConfig } from "../types";
export declare const resolveThemeConfig: (themeConfig: ResolvedHopeThemeConfig, rootLang: HopeLang) => void;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveThemeConfig = void 0;
const vuepress_shared_1 = require("@mr-hope/vuepress-shared");
const encrypt_1 = require("./encrypt");
const setThemeLocales = (themeConfig, rootLang) => {
const rootLangPath = (0, vuepress_shared_1.lang2Path)(rootLang);
// set locate for base
themeConfig.locales["/"] = Object.assign(Object.assign(Object.assign({}, (0, vuepress_shared_1.getLocale)(rootLang)), (themeConfig.locales[rootLangPath] || {})), (themeConfig.locales["/"] || {}));
// handle other languages
Object.keys(themeConfig.locales).forEach((path) => {
if (path === "/")
return;
const lang = (0, vuepress_shared_1.path2Lang)(path);
themeConfig.locales[path] = Object.assign(Object.assign({}, (0, vuepress_shared_1.getLocale)(lang)), themeConfig.locales[path]);
});
};
const resolveThemeConfig = (themeConfig, rootLang) => {
setThemeLocales(themeConfig, rootLang);
if (themeConfig.encrypt)
(0, encrypt_1.resolveEncrypt)(themeConfig.encrypt);
};
exports.resolveThemeConfig = resolveThemeConfig;
//# sourceMappingURL=themeConfig.js.map
\ No newline at end of file
{"version":3,"file":"themeConfig.js","sourceRoot":"","sources":["themeConfig.ts"],"names":[],"mappings":";;;AAAA,8DAA2E;AAC3E,uCAA2C;AAK3C,MAAM,eAAe,GAAG,CACtB,WAAoC,EACpC,QAAgB,EACV,EAAE;IACR,MAAM,YAAY,GAAG,IAAA,2BAAS,EAAC,QAAQ,CAAC,CAAC;IAEzC,sBAAsB;IACtB,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,iDACnB,IAAA,2BAAS,EAAC,QAAQ,CAAC,GACnB,CAAC,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,GACzC,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CACpC,CAAC;IAEF,yBAAyB;IACzB,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAChD,IAAI,IAAI,KAAK,GAAG;YAAE,OAAO;QAEzB,MAAM,IAAI,GAAG,IAAA,2BAAS,EAAC,IAAI,CAAC,CAAC;QAE7B,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,mCACpB,IAAA,2BAAS,EAAC,IAAI,CAAC,GACf,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAC7B,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEK,MAAM,kBAAkB,GAAG,CAChC,WAAoC,EACpC,QAAkB,EACZ,EAAE;IACR,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAEvC,IAAI,WAAW,CAAC,OAAO;QAAE,IAAA,wBAAc,EAAC,WAAW,CAAC,OAAO,CAAC,CAAC;AAC/D,CAAC,CAAC;AAPW,QAAA,kBAAkB,sBAO7B"}
\ No newline at end of file
{$contentClass}
code
color lighten($textColor, 20%)
padding 0.25rem 0.5rem
margin 0
font-size 0.85em
background-color rgba(27,31,35,0.05)
border-radius 3px
.token
&.deleted
color #EC5975
&.inserted
color $accentColor
{$contentClass}
pre, pre[class*="language-"]
line-height 1.4
padding 1.25rem 1.5rem
margin 0.85rem 0
background-color $codeBgColor
border-radius 6px
overflow auto
code
color #fff
padding 0
background-color transparent
border-radius 0
div[class*="language-"]
position relative
background-color $codeBgColor
border-radius 6px
.highlight-lines
user-select none
padding-top 1.3rem
position absolute
top 0
left 0
width 100%
line-height 1.4
.highlighted
background-color rgba(0, 0, 0, 66%)
pre, pre[class*="language-"]
background transparent
position relative
z-index 1
&::before
position absolute
z-index 3
top 0.8em
right 1em
font-size 0.75rem
color rgba(255, 255, 255, 0.4)
&:not(.line-numbers-mode)
.line-numbers-wrapper
display none
&.line-numbers-mode
.highlight-lines .highlighted
position relative
&:before
content ' '
position absolute
z-index 3
left 0
top 0
display block
width $lineNumbersWrapperWidth
height 100%
background-color rgba(0, 0, 0, 66%)
pre
padding-left $lineNumbersWrapperWidth + 1 rem
vertical-align middle
.line-numbers-wrapper
position absolute
top 0
width $lineNumbersWrapperWidth
text-align center
color rgba(255, 255, 255, 0.3)
padding 1.25rem 0
line-height 1.4
br
user-select none
.line-number
position relative
z-index 4
user-select none
font-size 0.85em
&::after
content ''
position absolute
z-index 2
top 0
left 0
width $lineNumbersWrapperWidth
height 100%
border-radius 6px 0 0 6px
border-right 1px solid rgba(0, 0, 0, 66%)
background-color $codeBgColor
for lang in $codeLang
div{'[class~="language-' + lang + '"]'}
&:before
content ('' + lang)
div[class~="language-javascript"]
&:before
content "js"
div[class~="language-typescript"]
&:before
content "ts"
div[class~="language-markup"]
&:before
content "html"
div[class~="language-markdown"]
&:before
content "md"
div[class~="language-json"]:before
content "json"
div[class~="language-ruby"]:before
content "rb"
div[class~="language-python"]:before
content "py"
div[class~="language-bash"]:before
content "sh"
div[class~="language-php"]:before
content "php"
@import '~prismjs/themes/prism-tomorrow.css'
@font-face {
font-family: "Crimson";
src: url(data:font/truetype;charset=utf-8;base64,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)
format("truetype");
font-weight: normal;
font-style: normal;
}
@require './normalize'
@require './prefer-color-scheme-config'
@require './theme'
@require './plugins/index'
@require './theme-color'
@require '~balloon-css/balloon.min.css'
@require '~@theme/styles/fonts/crimson.css'
@require '~@mr-hope/vuepress-shared/styles/wrapper'
html, body
padding 0
margin 0
background var(--bgcolor)
body
font-family Georgia Pro, Crimson, Georgia, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', STHeiti, 'Microsoft YaHei', SimSun, sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
font-display optional
font-size 16px
color var(--text-color)
min-height 100vh
-webkit-tap-highlight-color transparent
{$contentClass}:not(.custom)
@extend $wrapper
> *:first-child
margin-top $navbarHeight
a:hover
text-decoration underline
// unknow container
p.demo
padding 1rem 1.5rem
border 1px solid #ddd
border-radius 4px
img
max-width 100%
{$contentClass}.custom
padding 0
margin 0
img
max-width 100%
a
font-weight 500
color var(--accent-color)
text-decoration none
overflow-wrap break-word
p a code
font-weight 400
color var(--accent-color)
kbd
background #eee
border solid 0.15rem #ddd
border-bottom solid 0.25rem #ddd
border-radius 0.15rem
padding 0 0.15em
blockquote
font-size 1rem
color var(--light-grey)
border-left 0.2rem solid #dfe2e5
margin 1rem 0
padding 0.25rem 0 0.25rem 1rem
& > p
margin 0
ul, ol
padding-left 1.2em
strong
font-weight 600
h1, h2, h3, h4, h5, h6
font-weight 500
line-height 1.25
{$contentClass}:not(.custom) > &
margin-top 0.5rem - $navbarHeight
padding-top: ($navbarHeight + 1rem)
margin-bottom 0.5rem
&:first-child
margin-top -3rem
margin-bottom 1rem
+ p, + pre, + .custom-block
margin-top 2rem
&:hover .header-anchor
opacity 1
p
{$contentClass}:not(.custom) > &, {$contentClass}:not(.custom) > ul &, {$contentClass}:not(.custom) > ol &
text-align justify
word-break break-word
hyphens auto
overflow-wrap break-word
@media (max-width $MQMobileNarrow)
text-align left
h1
font-size 2rem
h2
font-size 1.65rem
padding-bottom 0.3rem
border-bottom 1px solid var(--border-color)
h3
font-size 1.35rem
a.header-anchor
font-size 0.85em
float left
margin-left -0.87em
padding-right 0.23em
margin-top 0.125em
opacity 0
transition opacity 0.2s
&:hover
text-decoration none
code, kbd, .line-number
font-family source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace
p, ul, ol
line-height 1.7
hr
border 0
border-top 1px solid var(--border-color)
table
border-collapse collapse
margin 1rem 0
display block
overflow-x auto
tr
border-top 1px solid #dfe2e5
&:nth-child(2n)
background #f6f8fa
th, td
border 1px solid var(--grey14)
padding 0.6em 1em
.theme-dark
tr:nth-child(2n)
background #252322
th, td
border 1px solid var(--grey12)
/* basic color */
$accentColor ?= #3eaf7c
$textColor ?= #242424
$darkTextColor ?= #9e9e9e
$bgColor ?= #fff
$darkBgColor ?= #1e1e1e
$bgColorLight ?= #f8f8f8
$darkBgColorLight ?= #272727
$bgColorBlur ?= rgba(255, 255, 255, 0.9)
$darkBgColorBlur ?= rgba(30, 30, 30, 0.9)
$borderColor ?= #eaecef
$darkBorderColor ?= #302d28
$codeBgColor ?= #282c34
$darkCodeBgColor ?= #282c34
$arrowBgColor ?= #ccc
$darkArrowBgColor ?= #333
/* colors provided by theme */
$boxShadowColor ?= #f0f1f2
$darkBoxShadowColor ?= #0f0e0d
$cardShadowColor ?= rgba(0, 0, 0, 0.15)
$darkCardShadowColor ?= rgba(0, 0, 0, 0.3)
/* * theme-color */
$colorPicker ?= {
red: #e74c3c,
blue: #3498db,
green: #3eaf7c,
orange: #f39c12,
purple: #8e44ad
}
/* badge color */
$badgeTipColor ?= #42b983
$badgeWarningColor ?= darken(#ffe564, 35%)
$badgeErrorColor ?= #DA5961
/* layout */
$navbarHeight ?= 3.6rem
$navbarMobileHeight ?= 3.25rem
$navbarHorizontalPadding ?= 1.5rem
$navbarMobileHorizontalPadding ?= 1rem
$navbarVerticalPadding ?= 0.7rem
$navbarMobileVerticalPadding ?= 0.5rem
$sidebarWidth ?= 18rem
$mobileSidebarWidth ?= $sidebarWidth
$contentWidth ?= 820px
$homePageWidth ?= 960px
/* responsive breakpoints */
$MQWide ?= 1440px // wide screen
$MQMedium ?= 1350px // medium screen
$MQNormal ?= 1280px // desktop
$MQNarrow ?= 959px // narrow desktop / iPad
$MQMobile ?= 719px // wide mobile
$MQMobileNarrow ?= 419px // narrow mobile
/* code block */
$lineNumbersWrapperWidth ?= 2.5rem
$codeLang ?= js ts html md vue css sass scss less stylus go java c sh yaml py docker dockerfile makefile
/* content class */
$contentClass ?= '.theme-default-content'
for $themeColorName, $themeColor in $colorPicker
.theme-{$themeColorName}
#nprogress
.bar
background $themeColor
.peg
box-shadow 0 0 10px $themeColor, 0 0 5px $themeColor
.spinner-icon
border-color $themeColor
.navbar
.search-box
height calc(2rem + 4px)
margin-left 0.25rem
input
margin-top 1px
margin-bottom 1px
border-color transparent
border-radius 0.25em
@media (max-width $MQMobile)
left 0
background-color transparent
@media (min-width $MQNarrow)
background-color #efeef4
&:focus
width 15rem
&:focus
background-color var(--bgcolor)
border-color var(--accent-color)
.theme-dark &
color var(--text-color)
background-color transparent
@media (min-width $MQNarrow)
background-color lighten($darkBgColor, 10%) !important
border-color var(--border-color)
&:focus
background-color lighten($darkBgColor, 10%) !important
.theme-dark &
.suggestion
a
color darken($darkTextColor, 35%)
&.focused
background #0c0b0a
a
color var(--accent-color)
border-color var(--accent-color)
.suggestions
border-color var(--border-color)
background var(--white)
#docsearch button
background-color transparent
border-color transparent
border-radius 0.25em
.mobile &
left 0
background-color transparent
@media (min-width $MQNarrow)
background-color #efeef4
&:focus
background-color var(--bgcolor)
border-color var(--accent-color)
.theme-dark &
color var(--text-color)
@media (min-width $MQNarrow)
background-color lighten($darkBgColor, 10%) !important
border-color var(--border-color)
&:focus
background-color lighten($darkBgColor, 10%) !important
body
--text-color $textColor
--bgcolor $bgColor
--bgcolor-light $bgColorLight
--border-color $borderColor
--code-bgcolor $codeBgColor
--arrow-bgcolor $arrowBgColor
--box-shadow-color $boxShadowColor
--card-shadow-color $cardShadowColor
--text-color-l10 lighten($textColor, 10%)
--text-color-l20 lighten($textColor, 20%)
--text-color-l25 lighten($textColor, 25%)
--text-color-l40 lighten($textColor, 40%)
--black #000
--dark-grey #666
--light-grey #999
--white #fff
--grey3 #333
--grey12 #bbb
--grey14 #eee
body.theme-light
--text-color $textColor
--bgcolor $bgColor
--bgcolor-light $bgColorLight
--bgcolor-blur $bgColorBlur
--border-color $borderColor
--code-bgcolor $codeBgColor
--arrow-bgcolor $arrowBgColor
--box-shadow-color $boxShadowColor
--card-shadow-color $cardShadowColor
--text-color-l10 lighten($textColor, 10%)
--text-color-l20 lighten($textColor, 20%)
--text-color-l25 lighten($textColor, 25%)
--text-color-l40 lighten($textColor, 40%)
--black #000
--dark-grey #666
--light-grey #999
--white #fff
--grey3 #333
--grey12 #bbb
--grey14 #eee
body.theme-dark
--text-color $darkTextColor
--bgcolor $darkBgColor
--bgcolor-light $darkBgColorLight
--bgcolor-blur $darkBgColorBlur
--border-color $darkBorderColor
--code-bgcolor $darkCodeBgColor
--arrow-bgcolor $darkArrowBgColor
--box-shadow-color $darkBoxShadowColor
--card-shadow-color $darkCardShadowColor
--text-color-l10 lighten($darkTextColor, 10%)
--text-color-l20 lighten($darkTextColor, 20%)
--text-color-l25 lighten($darkTextColor, 25%)
--text-color-l40 lighten($darkTextColor, 40%)
--black #fff
--dark-grey #999
--light-grey #666
--white #000
--grey3 #ccc
--grey12 #333
--grey14 #111
body
--accent-color $accentColor
--accent-color-l10 lighten($accentColor, 10%)
--accent-color-d10 darken($accentColor, 10%)
--accent-color-a15 alpha($accentColor, 15%)
theme-color($themeColor, $colorName)
body.theme-{$colorName}
--accent-color $themeColor
--accent-color-l10 lighten($themeColor, 10%)
--accent-color-d10 darken($themeColor, 10%)
--accent-color-a15 alpha($themeColor, 15%)
for key, value in $colorPicker
theme-color(value, key)
@require './code/index'
.theme-container:not(.has-navbar)
{$contentClass}:not(.custom) > h1, h2, h3, h4, h5, h6
margin-top 1.5rem
padding-top 0
// narrow mobile
@media (max-width $MQMobileNarrow)
h1
font-size 1.7rem
h2
font-size 1.5rem
h3
font-size 1.3rem
{$contentClass}
div[class*='language-']
margin 0.85rem -1.5rem
border-radius 0
.iconfont
font-weight normal
[aria-label][data-balloon-pos]
cursor help
declare module "*.jpg" {
const path: string;
export default path;
}
declare module "docsearch.js/dist/cdn/docsearch.min.js" {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const docsearch: any;
export default docsearch;
}
declare module "@AlgoliaSearchBox" {
import vue from "vue";
export default vue;
}
declare module "@BlogHome" {
import vue from "vue";
export default vue;
}
declare module "@BlogInfo" {
import vue from "vue";
export default vue;
}
declare module "@BloggerInfo" {
import vue from "vue";
export default vue;
}
declare module "@BlogPage" {
import vue from "vue";
export default vue;
}
declare module "@Comment" {
import vue from "vue";
export default vue;
}
declare module "@ContentTop" {
import vue from "vue";
export default vue;
}
declare module "@ContentBottom" {
import vue from "vue";
export default vue;
}
declare module "@NavbarStart" {
import vue from "vue";
export default vue;
}
declare module "@NavbarCenter" {
import vue from "vue";
export default vue;
}
declare module "@NavbarEnd" {
import vue from "vue";
export default vue;
}
declare module "@PageTop" {
import vue from "vue";
export default vue;
}
declare module "@PageInfo" {
import vue from "vue";
export default vue;
}
declare module "@PageBottom" {
import vue from "vue";
export default vue;
}
declare module "@Pagination" {
import vue from "vue";
export default vue;
}
declare module "@SearchBox" {
import vue from "vue";
export default vue;
}
declare module "@ThemeColor" {
import vue from "vue";
export default vue;
}
declare module "@SearchBox" {
import vue from "vue";
export default vue;
}
declare module "@SidebarTop" {
import vue from "vue";
export default vue;
}
declare module "@SidebarCenter" {
import vue from "vue";
export default vue;
}
declare module "@SidebarBottom" {
import vue from "vue";
export default vue;
}
/* eslint-disable @typescript-eslint/no-explicit-any */
import { HopeThemeLocaleConfigItem } from "@mr-hope/vuepress-shared";
import {
BlogMedia,
HopeThemeConfig,
HopeNavBarConfig,
HopeSideBarConfig,
HopeFooterConfig,
} from "./theme";
import { PageInfotype } from "@mr-hope/vuepress-plugin-comment";
import { FeedFrontmatterOption } from "@mr-hope/vuepress-plugin-feed";
import { AlgoliaOption } from "@mr-hope/vuepress-types";
declare module "vue/types/vue" {
export interface Vue {
$category: any;
$tag: any;
$currentTag: any;
$currentCategory: any;
$pagination: any;
}
}
declare module "@mr-hope/vuepress-types" {
interface PageFrontmatter {
icon?: string;
author?: string | false;
original?: boolean;
/**
* @deprecated
*/
date?: Date | string;
time?: Date | string;
category?: string;
tag?: string[];
/**
* @deprecated
*/
tags?: string[];
summary?: string;
sticky?: boolean | number;
star?: boolean | number;
article?: boolean;
timeline?: boolean;
password?: string | number;
image?: string;
copyright?: {
minLength?: number;
noCopy?: boolean;
noSelect?: boolean;
};
feed?: FeedFrontmatterOption;
pageInfo?: PageInfotype[] | false;
visitor?: boolean;
breadcrumb?: boolean;
breadcrumbIcon?: boolean;
navbar?: boolean;
sidebar?: "auto" | boolean;
sidebarDepth?: number;
comment?: boolean;
editLink?: boolean;
contributor?: boolean;
updateTime?: boolean;
prev?: string | false;
next?: string | false;
footer?: string | boolean;
copyrightText?: string | false;
mediaLink?: BlogMedia;
search?: boolean;
backToTop?: boolean;
anchorDisplay?: boolean;
}
interface I18nConfig extends Partial<HopeThemeLocaleConfigItem> {
/** 导航栏链接 */
nav?: HopeNavBarConfig;
/** 侧边栏配置 */
sidebar?: HopeSideBarConfig;
/** 当前语言的 algolia 设置 */
algolia?: AlgoliaOption;
/** 页脚设置 */
footer?: HopeFooterConfig;
}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface ThemeConfig extends HopeThemeConfig {}
interface Page {
_chunkName?: string;
}
interface ResolvedComponent {
_chunkName?: string;
}
}
import {
HopeNavBarConfig,
HopeSideBarConfig,
HopeThemeConfig,
HopeVuePressConfig,
ResolvedHopeVuePressConfig,
} from "./theme";
import "./declare";
import "./extend";
export * from "./theme";
export const config: (config: HopeVuePressConfig) => ResolvedHopeVuePressConfig;
export const themeConfig: (themeConfig: HopeThemeConfig) => HopeThemeConfig;
export const navbarConfig: (navbarConfig: HopeNavBarConfig) => HopeNavBarConfig;
export const sidebarConfig: (
sidebarConfig: HopeSideBarConfig
) => HopeSideBarConfig;
export interface HopeThemeAppearanceConfig {
/**
* Algolia 搜索类型
*
* Algolia Search Type
*
* @default 'dropdown'
*/
algoliaType?: "dropdown" | "full";
/**
* 图标前缀
*
* Prefix of icon class
*
* @default 'icon-'
*/
iconPrefix?: string;
/**
* 是否在移动视图下隐藏站点名称
*
* Whether hide site title on mobile
*
* @default true
*/
hideSiteTitleonMobile?: boolean;
/**
* 是否在导航栏显示仓库链接
*
* Whether display repo link in navbar
*
* @default true
*/
repoDisplay?: boolean;
/**
* 是否显示 ”全屏“ 按钮
*
* Whether show fullscreen button in navbar
*
* @default true
*/
fullscreen?: boolean;
/**
* 是否在侧边栏显示图标
*
* Whether show icons in the sidebar
*
* @default true
*/
sidebarIcon?: boolean;
/**
* 侧边栏嵌套的标题深度
*
* Nested headings depth in sidebar
*
* @default 2
*/
sidebarDepth?: number;
/**
* 是否在路径导航显示图标
*
* Whether display icon in breadcrumb
*
* @default true
*/
breadcrumbIcon?: boolean;
/**
* 是否显示当前页面贡献者
*
* Whether show contributors in each page
*
* @default true
*/
contributor?: boolean;
/**
* 显示编辑本页链接
*
* Whether show edit link on each page
*
* @default true
*/
editLinks?: boolean;
/**
* 显示更新时间
*
* Whether show update time on each page
*
* @default true
*/
updateTime?: boolean;
/**
* 是否显示返回顶部按钮
*
* 如果设置为数字,则该数字为触发临界值 (默认临界值为 300px)
*
* Wether display backto top button
*
* If it’s set with a number, then it will be the threshold
*
* @default true
*/
backToTop?: boolean | number;
}
import {
NavBarConfigItem,
SideBarConfigItemObject,
} from "@mr-hope/vuepress-types";
/** vuepress-theme-hope 导航栏配置项 */
export interface HopeNavBarConfigItem extends NavBarConfigItem {
/** 导航栏对应项的图标 */
icon?: string;
/** 导航栏的路径前缀 */
prefix?: string;
/** 导航栏下拉列表子项 */
items?: HopeNavBarConfigItem[];
}
/** vuepress-theme-hope 导航栏配置 */
export type HopeNavBarConfig = HopeNavBarConfigItem[] | false;
/** vuepress-theme-hope 侧边栏配置对象 */
export interface HopeSideBarConfigItemObject extends SideBarConfigItemObject {
/** 分组的图标 */
icon?: string;
/** 当前分组的路径前缀 */
prefix?: string;
/** 当前侧边栏的子项 */
children: HopeSideBarConfigItem[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[props: string]: any;
}
/** vuepress-theme-hope 侧边栏配置项 */
export type HopeSideBarConfigItem =
| string
| string[]
| HopeSideBarConfigItemObject;
/** vuepress-theme-hope 侧边栏配置 */
export type HopeSideBarConfig =
| HopeSideBarConfigItem[]
| Record<string, HopeSideBarConfigItem[]>
| "auto"
| false;
/**
* 合法的媒体
*
* media you can choose
*/
type BlogMedia =
| "Baidu"
| "Bitbucket"
| "Dingding"
| "Discord"
| "Dribbble"
| "Email"
| "Evernote"
| "Facebook"
| "Flipboard"
| "Gitee"
| "Github"
| "Gitlab"
| "Gmail"
| "Instagram"
| "Lines"
| "Linkedin"
| "Pinterest"
| "Pocket"
| "QQ"
| "Qzone"
| "Reddit"
| "Rss"
| "Steam"
| "Twitter"
| "Wechat"
| "Weibo"
| "Whatsapp"
| "Youtube"
| "Zhihu";
/**
* 博客选项
*
* Blog configuration
*/
export type BlogOptions = {
/**
* 博主名称
*
* Name of the Blogger, default is author
*/
name?: string;
/**
* 博主头像,应为绝对路径
*
* Blogger avator, must be an absolute path
*/
avatar?: string;
/**
* 博主的个人介绍地址
*
* Intro page about blogger
*/
intro?: string;
/**
* 媒体链接配置
*
* Media links configuration
*
* E.g.
*
* ```js
* {
* QQ: "http://wpa.qq.com/msgrd?v=3&uin=1178522294&site=qq&menu=yes",
* Qzone: "https://1178522294.qzone.qq.com/",
* Gmail: "mailto:zhangbowang1998@gmail.com",
* Zhihu: "https://www.zhihu.com/people/mister-hope",
* Steam: "https://steamcommunity.com/id/Mr-Hope/",
* Weibo: "https://weibo.com/misterhope",
* }
* ```
*/
links?: Partial<Record<BlogMedia, string>>;
/**
* 是否剪裁头像为圆形形状
*
* Whether cliping the avatar with round shape
*
* @default true
*/
roundAvatar?: boolean;
/**
* 是否在侧边栏展示博主信息
*
* Whether to display blogger info in sidebar
*
* @default 'none'
*/
sidebarDisplay?: "mobile" | "none" | "always";
/**
* 时间轴自定义文字
*
* Custom text for timeline
*
* @default 'Yesterday once more'
*/
timeline?: string;
/**
* 每页的文章数量
*
* Article number per page
*
* @default 10
*/
perPage?: number;
};
/**
* 加密选项
*
* Encrypt Options
*/
export interface EncryptOptions {
/**
* 功能状态
*
* - `'global'` 意味着全局启用
* - `'local'` 意味着全局禁用,可在页面内启用
*
* Feature Status
*
* - `'global'` means enabled globally
* - `'local'` means disabled globally and can be enabled in pages
*
* @default 'local'
*/
status?: "global" | "local";
/**
* 最高权限密码
*
* Global passwords, which has the highest authority
*/
global?: string | string[];
/**
* 加密配置
*
* ```json
* {
* // 这会加密整个 guide 目录,并且两个密码都是可用的
* "/guide/": ["1234", "5678"],
* // 这只会加密 config/page.html
* "/config/page.html": "1234"
* }
* ```
*
* Encrypt Configuration
*
* E.g.:
*
* ```json
* {
* // This will encrypt the entire guide directory and both passwords will be available
* "/guide/": ["1234", "5678"],
* // this will only encrypt config/page.html
* "/config/page.html": "1234"
* }
* ```
*/
config?: Record<string, string | string[]>;
}
/** 自定义布局配置 */
export interface CustomOptions {
/** 页面顶部插槽 */
pageTop?: string;
/** 文章内容顶部插槽 */
contentTop?: string;
/** 文章内容底部插槽 */
contentBottom?: string;
/** 页面底部插槽 */
pageBottom?: string;
/** 导航栏起始插槽 */
navbarStart?: string;
/** 导航栏中部插槽 */
navbarCenter?: string;
/** 导航栏结束插槽 */
navbarEnd?: string;
/** 侧边栏顶部插槽 */
sidebarTop?: string;
/** 侧边栏中部插槽 */
sidebarCenter?: string;
/** 侧边栏底部插槽 */
sidebarBottom?: string;
}
export interface HopeFeatureConfig {
/**
* 深色模式支持选项:
*
* - `'auto-switch'`: "关闭 | 自动 | 打开" 的三段式开关 (默认)
* - `'switch'`: "关闭 | 打开" 的切换式开关
* - `'auto'`: 自动根据用户设备主题或当前时间决定是否应用深色模式
* - `'disable'`: 禁用深色模式
*
* Dark mode support options:
*
* - `'auto-switch'`: "off | automatic | on" three-stage switch (Default)
* - `'switch'`: "Close | Open" toggle switch
* - `'auto'`: Automatically decide whether to apply dark mode based on user device’s color-scheme or current time
* - `'disable'`: disable dark mode
*
* @default 'auto-switch'
*/
darkmode?: "auto-switch" | "auto" | "switch" | "disable";
/**
* 主题色选项配置。
*
* Theme color configuration.
*
* E.g.:
* ```js
* {
* blue: '#2196f3',
* red: '#f26d6d',
* green: '#3eaf7c',
* orange: '#fb9b5f'
* }
* ```
*
* @default { blue: '#2196f3', red: '#f26d6d', green: '#3eaf7c', orange: '#fb9b5f' }
*/
themeColor?: Record<string, string> | false;
/**
* 博客设置
*
* Blog configuration
*/
blog?: BlogOptions | false;
/**
* 加密设置
*
* Encrypt Configuration
*/
encrypt?: EncryptOptions;
/**
* 自定义组件设置
*/
custom?: CustomOptions;
/**
* 是否启用平滑滚动
*
* Enable smooth scrolling feature
*
* @default true
*/
smoothScroll?: boolean;
/**
* 每分钟的阅读字数
*
* Reading speed of word per minute
*
* @default 300
*/
wordPerminute?: number;
}
import { HopeThemeConfig, ResolvedHopeThemeConfig } from "./theme";
import { SiteConfig } from "@mr-hope/vuepress-types";
export * from "./appearance";
export * from "./extends";
export * from "./feature";
export * from "./layout";
export * from "./locale";
export * from "./plugin";
export * from "./theme";
/** vuepress-theme-hope 项目配置 */
export interface HopeVuePressConfig extends SiteConfig {
/** 自定义主题的配置 */
themeConfig: HopeThemeConfig;
}
/** 处理过的 vuepress-theme-hope 项目配置 */
export interface ResolvedHopeVuePressConfig extends HopeVuePressConfig {
/** 使用的自定义主题 */
theme: "hope";
/** 自定义主题的配置 */
themeConfig: ResolvedHopeThemeConfig;
}
import { PageInfotype } from "@mr-hope/vuepress-plugin-comment";
import { HopeNavBarConfig, HopeSideBarConfig } from "./extends";
/**
* 页脚配置
*
* Footer Settings
*/
export interface HopeFooterConfig {
/**
* 页脚的默认内容,可输入 HTMLString
*
* The default content for the footer, can accept HTMLString.
*/
content?: string;
/**
* 默认的版权信息,设置为 `false` 来默认禁用它
*
* The default copyright info, set it to `false` to disable it by default.
*/
copyright?: string | false;
/**
* 是否默认显示页脚
*
* Whether to display footer by default
*
* @default false
*/
display?: boolean;
}
export interface HopeLayoutConfig {
/**
* 导航栏配置
*
* Navbar configuration
*/
nav?: HopeNavBarConfig;
/**
* 是否禁用导航栏
*
* Whether disable navbar
*
* @default false
*/
navbar?: boolean;
/**
* 是否在向下滚动时自动隐藏导航栏
*
* Whether to hide navbar when scrolling down
*
* @default 'mobile'
*/
navAutoHide?: "always" | "mobile" | "none";
/**
* 侧边栏配置
*
* Sidebar configuration
*/
sidebar?: HopeSideBarConfig;
/**
* 是否在桌面模式显示锚点标题
*
* Whether display anchor in desktop mode
*
* @default true
*/
anchorDisplay?: boolean;
/**
* 是否全局启用路径导航
*
* Whether enable breadcrumb globally
*
* @default true
*/
breadcrumb?: boolean;
/**
* 页面信息
*
* Article information
*
* Avaliable Options:
*
* - `'author'`: Author
* - `'time'`: Writing Date
* - `'category'`: Category
* - `'tag'`: Tags
* - `'reading-time'`: Expect reading time
* - `'word'`: Word number for the article
* - `'visitor'`: Visitor Number
*
* @default ['author', 'visitor', 'time', 'category', 'tag', 'reading-time']
*/
pageInfo?: PageInfotype[] | false;
/**
* 页脚配置
*
* Footer Configuration
*/
footer?: HopeFooterConfig;
}
import {
HopeNavBarConfig,
HopeSideBarConfig,
HopeThemeLocaleConfigItem,
} from "@mr-hope/vuepress-shared";
import { AlgoliaOption } from "@mr-hope/vuepress-types";
import { HopeFooterConfig } from "./layout";
/** vuepress-theme-hope 多语言配置 */
export interface HopeLangLocalesConfig
extends Partial<HopeThemeLocaleConfigItem> {
/** 当前语言下的标题 */
title?: string;
/** 当前语言下的描述 */
description?: string;
/** 导航栏链接 */
nav?: HopeNavBarConfig;
/** 侧边栏配置 */
sidebar?: HopeSideBarConfig;
/** 当前语言的 algolia 设置 */
algolia?: AlgoliaOption;
/** 页脚设置 */
footer?: HopeFooterConfig;
}
import { ActiveHashOptions } from "vuepress-plugin-active-hash";
import { CommentOptions } from "@mr-hope/vuepress-plugin-comment";
import { CopyCodeOptions } from "@mr-hope/vuepress-plugin-copy-code";
import { FeedOptions } from "@mr-hope/vuepress-plugin-feed";
import { GitOptions } from "@mr-hope/vuepress-plugin-git";
import { MarkdownEnhanceOptions } from "vuepress-plugin-md-enhance";
import { PWAOptions } from "@mr-hope/vuepress-plugin-pwa";
import { PhotoSwipeOptions } from "vuepress-plugin-photo-swipe";
import { SeoOptions } from "@mr-hope/vuepress-plugin-seo";
import { SitemapOptions } from "@mr-hope/vuepress-plugin-sitemap";
import { SmoothScrollOptions } from "@mr-hope/vuepress-plugin-smooth-scroll";
import type { Page, ResolvedComponent } from "@mr-hope/vuepress-types";
/**
* 重命名块选项
*
* Options for renaming chunks
*/
export interface ChunkRenameOptions {
/**
* 页面块重命名选项。 默认情况下,所有页面块都将以页面标题命名。
*
* Page Chunk Rename Option. By default, all page chunks will be named with page title.
*/
pageChunkName: ((page: Page) => string) | false;
/**
* 布局块重命名选项。 默认情况下,所有布局块都将通过其组件名称来命名。
*
* Layout Chunk Rename Option. By default, all the layout chunks will be named by their component name.
*/
layoutChunkName: ((layout: ResolvedComponent) => string) | false;
}
/**
* Options for cleaning url suffix
*/
export interface CleanUrlOptions {
/**
* 普通页面后缀。此默认行为将为 `/a/b.md` 生成 `/a/b`。
*
* Nornal Page suffix. This default behavior will generate `a/b.md` with `/a/b`.
*
* @default ''
*/
normalSuffix: string;
/**
* `index.md`,`readme.md` 和 `README.md` 的页面后缀。此默认行为将为 `a/readme.md` 生成 `/a/`。
*
* Page suffix for `index.md`, `readme.md` and `README.md`. This default behavior will generate `a/readme.md` with `/a/`.
*
* @default '/'
*/
indexSuffix: string;
/**
* 未找到页面的链接
*
* Link for not found pages
*
* @default './404.html'
*/
notFoundPath: string;
}
/**
* 版权设置
*
* Copyright Settings
*/
export interface HopeCopyrightConfig {
/**
* 功能状态
*
* - `'global'` 意味着全局启用
* - `'local'` 意味着全局禁用,可在页面内启用
*
* Feature Status
*
* - `'global'` means enabled globally
* - `'local'` means disabled globally and can be enabled in pages
*
* @default 'global'
*/
status?: "global" | "local";
/**
* 触发版权信息或禁止复制动作的最少字符数
*
* The minimum text length that triggers the clipboard component or the noCopy effect
*/
minLength?: number;
/**
* 是否禁止复制
*
* Whether to prohibit copying.
*/
noCopy?: boolean;
/**
* 是否禁止选中文字
*
* Whether to prohibit selecting.
*/
noSelect?: boolean;
}
interface HopeThemePluginConfig {
/**
* AddThis 的公共 ID
* @see http://vuepress-theme-hope.github.io/add-this/zh/config/
*
* pubid for addthis
* @see http://vuepress-theme-hope.github.io/add-this/config/
*/
addThis?: string;
activeHash?: ActiveHashOptions | false;
/**
* 评论插件配置
* @see http://vuepress-theme-hope.github.io/comment/zh/config/
*
* Comment plugin options
* @see http://vuepress-theme-hope.github.io/comment/config/
*/
comment?: CommentOptions;
/**
* chunk 重命名
*
* @see https://vuepress-theme-hope.github.io/zh/config/theme/plugin/#chunkrename
*
* Chunk Rename
* @see https://vuepress-theme-hope.github.io/config/theme/plugin/#chunkrename
*/
chunkRename?: ChunkRenameOptions | false;
/**
* 清理插件配置
* @see https://vuepress-theme-hope.github.io/zh/config/theme/plugin/#cleanurl
*
* Clean Url Config
* @see https://vuepress-theme-hope.github.io/config/theme/plugin/#cleanurl
*/
cleanUrl?: CleanUrlOptions | false;
/**
* 代码复制插件配置
* @see http://vuepress-theme-hope.github.io/copy-code/zh/config/
*
* code copy plugin options
* @see http://vuepress-theme-hope.github.io/copy-code/config/
*/
copyCode?: CopyCodeOptions | false;
/**
* 版权设置
*
* Copyright plugin options
*/
copyright?: HopeCopyrightConfig;
/**
* Feed 插件配置
* @see http://vuepress-theme-hope.github.io/feed/zh/config/
*
* Feed plugin options
* @see http://vuepress-theme-hope.github.io/feed/config/
*/
feed?: FeedOptions | false;
/**
* Git 插件配置
* @see http://vuepress-theme-hope.github.io/git/zh/
*
* Git plugin options
* @see http://vuepress-theme-hope.github.io/git/
*/
git?: GitOptions | false;
/**
* Markdown 增强插件配置
* @see http://vuepress-theme-hope.github.io/md-enhance/zh/config/
*
* Markdown enhance plugin options
* @see http://vuepress-theme-hope.github.io/md-enhance/config/
*/
mdEnhance?: MarkdownEnhanceOptions | false;
/**
* PWA 插件配置
* @see http://vuepress-theme-hope.github.io/pwa/zh/config/
*
* PWA plugin options
* @see http://vuepress-theme-hope.github.io/pwa/config/
*/
pwa?: PWAOptions | false;
/**
* 图片预览插件配置
* @see http://vuepress-theme-hope.github.io/photo-swipe/zh/config/
*
* Photo Swipe plugin options
* @see http://vuepress-theme-hope.github.io/photo-swipe/config/
*/
photoSwipe?: PhotoSwipeOptions | false;
/**
* SEO 插件配置
* @see http://vuepress-theme-hope.github.io/seo/zh/config/
*
* SEO plugin options
* @see http://vuepress-theme-hope.github.io/seo/config/
*/
seo?: SeoOptions | false;
/**
* Sitemap 插件配置
* @see http://vuepress-theme-hope.github.io/sitemap/zh/config/
*
* Sitemap plugin options
* @see http://vuepress-theme-hope.github.io/sitemap/config/
*/
sitemap?: SitemapOptions | false;
smoothScrollOptions?: SmoothScrollOptions | number | false;
/**
* ts-loader 选项
*
* Options which will passed to ts-loader
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
typescript?: Record<string, any> | boolean;
}
import { AlgoliaOption, I18nConfig } from "@mr-hope/vuepress-types";
import { HopeThemeAppearanceConfig } from "./appearance";
import { HopeFeatureConfig } from "./feature";
import { HopeLayoutConfig } from "./layout";
import { HopeLangLocalesConfig } from "./locale";
import { HopeThemePluginConfig } from "./plugin";
/** vuepress-theme-hope 主题配置 */
export interface HopeThemeConfig
extends HopeThemeAppearanceConfig,
HopeFeatureConfig,
HopeLayoutConfig,
HopeThemePluginConfig {
/** 导航栏 Logo,应为绝对路径 */
logo?: string;
/**
* 暗黑模式下 logo
*
* Logo Image under darkmode
*/
darkLogo?: string;
/** 显示所有页面的标题链接 */
displayAllHeaders?: boolean;
/** 是否启用默认的搜索框 */
search?: boolean;
/** 搜索框占位符 */
searchPlaceholder?: string;
/** 默认搜索框显示的搜索结果数量 */
searchMaxSuggestions?: number;
/** Algolia 搜索配置 */
algolia?: AlgoliaOption;
/** 所有页面的 下一篇 链接 */
nextLinks?: boolean;
/** 所有页面的 上一篇 链接 */
prevLinks?: boolean;
/** 项目仓库地址 */
repo?: string;
/** 仓库标签文字 */
repoLabel?: string;
/** 文档所属仓库 */
docsRepo?: string;
/** 文档所属文件夹 */
docsDir?: string;
/** 文档所属分支 */
docsBranch?: string;
/**
* 多语言配置
*
* i18n config
*/
locales?: Record<string, I18nConfig & HopeLangLocalesConfig>;
/** 站点地址 */
hostname?: string;
/**
* 文章显示的默认作者
*
* The default author of the article
*/
author?: string;
}
/** 处理后的 vuepress-theme-hope 主题配置 */
export interface ResolvedHopeThemeConfig extends HopeThemeConfig {
/** 侧边栏深度 */
sidebarDepth: number;
/** 图标 FontClass 前缀 */
iconPrefix: string;
/** 多语言配置 */
locales: Record<string, I18nConfig & HopeLangLocalesConfig>;
/** 页脚配置 */
footer: HopeFooterConfig;
/** 显示编辑本页链接 */
editLinks: boolean;
}
import type { PageComputed } from "@mr-hope/vuepress-types";
export declare const getDate: (date: string | number | Date) => (number | undefined)[];
export declare const compareDate: (dataA: Date | number | string | undefined, dataB: Date | number | string | undefined) => number;
export declare const filterArticle: (pages: PageComputed[], filterFunc?: ((page: PageComputed) => boolean) | undefined) => PageComputed[];
export declare const sortArticle: (pages: PageComputed[], compareKey?: "sticky" | "star" | undefined) => PageComputed[];
export declare const generatePagination: (pages: PageComputed[], perPage?: number) => PageComputed[][];
import * as dayjs from "dayjs";
export const getDate = (date) => {
const time = dayjs(date instanceof Date || typeof date === "number" ? date : date.trim());
if (time.isValid()) {
const year = time.year();
const month = time.month() + 1;
const date = time.date();
const hour = time.hour();
const minute = time.minute();
const second = time.second();
const millisecond = time.millisecond();
if ((hour === 8 || hour === 0) &&
minute === 0 &&
second === 0 &&
millisecond === 0)
return [year, month, date, undefined, undefined, undefined];
return [year, month, date, hour, minute, second];
}
const pattern = /(?:(\d+)[/-](\d+)[/-](\d+))?\s*(?:(\d+):(\d+)(?::(\d+))?)?/u;
const [, year, month, day, hour, minute, second] = pattern.exec(date.trim()) || [];
const getNumber = (a) => typeof a === "undefined" ? undefined : Number(a);
const getYear = (yearNumber) => yearNumber && yearNumber < 100 ? yearNumber + 2000 : yearNumber;
const getSecond = (secondNumber) => hour && minute && !second ? 0 : secondNumber;
return [
getYear(getNumber(year)),
getNumber(month),
getNumber(day),
getNumber(hour),
getNumber(minute),
getSecond(getNumber(second)),
];
};
export const compareDate = (dataA, dataB) => {
if (!dataA)
return 1;
if (!dataB)
return -1;
const compare = (a, b) => {
if (a.length === 0)
return 0;
if (typeof b[0] === "undefined")
return typeof a[0] === "undefined" || a[0] === 0 ? 0 : -1;
if (typeof a[0] === "undefined")
return b[0] === 0 ? 0 : 1;
if (b[0] - a[0] === 0) {
a.shift();
b.shift();
return compare(a, b);
}
return b[0] - a[0];
};
return compare(getDate(dataA), getDate(dataB));
};
export const filterArticle = (pages, filterFunc) => pages.filter((page) => {
const { frontmatter: { article, blogpage, home }, title, } = page;
return (typeof title !== "undefined" &&
blogpage !== true &&
home !== true &&
article !== false &&
(!filterFunc || filterFunc(page)));
});
export const sortArticle = (pages, compareKey) => pages.slice(0).sort((prev, next) => {
if (compareKey) {
const prevKey = prev.frontmatter[compareKey];
const nextKey = next.frontmatter[compareKey];
if (prevKey && nextKey && prevKey !== nextKey)
return Number(nextKey) - Number(prevKey);
if (prevKey && !nextKey)
return -1;
if (!prevKey && nextKey)
return 1;
}
const prevTime = prev.frontmatter.time || prev.frontmatter.date || prev.createTimeStamp;
const nextTime = next.frontmatter.time || next.frontmatter.date || next.createTimeStamp;
return compareDate(prevTime, nextTime);
});
export const generatePagination = (pages, perPage = 10) => {
const result = [];
let index = 0;
while (index < pages.length) {
const paginationPage = [];
for (let i = 0; i < perPage; i++)
if (index < pages.length) {
paginationPage.push(pages[index]);
index += 1;
}
result.push(paginationPage);
}
return result;
};
//# sourceMappingURL=article.js.map
\ No newline at end of file
{"version":3,"file":"article.js","sourceRoot":"","sources":["article.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,MAAM,CAAC,MAAM,OAAO,GAAG,CACrB,IAA4B,EACJ,EAAE;IAC1B,MAAM,IAAI,GAAG,KAAK,CAChB,IAAI,YAAY,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CACtE,CAAC;IAEF,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE;QAClB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAEvC,IACE,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC;YAC1B,MAAM,KAAK,CAAC;YACZ,MAAM,KAAK,CAAC;YACZ,WAAW,KAAK,CAAC;YAEjB,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QAE9D,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;KAClD;IAED,MAAM,OAAO,GAAG,6DAA6D,CAAC;IAC9E,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,GAC9C,OAAO,CAAC,IAAI,CAAE,IAAe,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;IAE9C,MAAM,SAAS,GAAG,CAAC,CAAS,EAAsB,EAAE,CAClD,OAAO,CAAC,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAEnD,MAAM,OAAO,GAAG,CAAC,UAA8B,EAAsB,EAAE,CACrE,UAAU,IAAI,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC;IAElE,MAAM,SAAS,GAAG,CAAC,YAAgC,EAAsB,EAAE,CACzE,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC;IAE/C,OAAO;QACL,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;QACxB,SAAS,CAAC,KAAK,CAAC;QAChB,SAAS,CAAC,GAAG,CAAC;QACd,SAAS,CAAC,IAAI,CAAC;QACf,SAAS,CAAC,MAAM,CAAC;QACjB,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;KAC7B,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,KAAyC,EACzC,KAAyC,EACjC,EAAE;IACV,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IACrB,IAAI,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC,CAAC;IAEtB,MAAM,OAAO,GAAG,CACd,CAAyB,EACzB,CAAyB,EACjB,EAAE;QACV,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAC7B,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,WAAW;YAC7B,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,WAAW,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5D,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,WAAW;YAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAE3D,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE;YACrB,CAAC,CAAC,KAAK,EAAE,CAAC;YACV,CAAC,CAAC,KAAK,EAAE,CAAC;YAEV,OAAO,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SACtB;QACD,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC,CAAC;IAEF,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,KAAqB,EACrB,UAA4C,EAC5B,EAAE,CAClB,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;IACpB,MAAM,EACJ,WAAW,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EACxC,KAAK,GACN,GAAG,IAAI,CAAC;IAET,OAAO,CACL,OAAO,KAAK,KAAK,WAAW;QAC5B,QAAQ,KAAK,IAAI;QACjB,IAAI,KAAK,IAAI;QACb,OAAO,KAAK,KAAK;QACjB,CAAC,CAAC,UAAU,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAClC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,KAAqB,EACrB,UAA8B,EACd,EAAE,CAClB,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE;IACjC,IAAI,UAAU,EAAE;QACd,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QAE7C,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO,KAAK,OAAO;YAC3C,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC3C,IAAI,OAAO,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,CAAC,CAAC;QACnC,IAAI,CAAC,OAAO,IAAI,OAAO;YAAE,OAAO,CAAC,CAAC;KACnC;IAED,MAAM,QAAQ,GACZ,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,CAAC,eAAe,CAAC;IACzE,MAAM,QAAQ,GACZ,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,IAAI,IAAI,CAAC,eAAe,CAAC;IAEzE,OAAO,WAAW,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AACzC,CAAC,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,KAAqB,EACrB,OAAO,GAAG,EAAE,EACM,EAAE;IACpB,MAAM,MAAM,GAAqB,EAAE,CAAC;IACpC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,OAAO,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE;QAC3B,MAAM,cAAc,GAAmB,EAAE,CAAC;QAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE;YAC9B,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE;gBACxB,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;gBAClC,KAAK,IAAI,CAAC,CAAC;aACZ;QAEH,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;KAC7B;IAED,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
\ No newline at end of file
import type { DirectiveOptions, VNode } from "vue";
import type { DirectiveBinding } from "vue/types/options";
declare type Event = TouchEvent | MouseEvent;
interface PopupHtmlElement extends HTMLElement {
$vueClickOutside?: {
callback: (event: Event) => void;
handler: (event: Event) => void;
};
}
declare type PopupDirectiveFunction = (el: PopupHtmlElement, binding: DirectiveBinding, vnode: VNode, oldVnode: VNode) => void;
export declare const bind: PopupDirectiveFunction;
export declare const update: PopupDirectiveFunction;
export declare const unbind: PopupDirectiveFunction;
declare const _default: DirectiveOptions;
export default _default;
const validate = (binding) => {
if (typeof binding.value !== "function") {
console.warn("[Vue-click-outside:] provided expression", binding.expression, "is not a function.");
return false;
}
return true;
};
const isPopup = (popupItem, elements) => {
if (!popupItem || !elements)
return false;
for (let i = 0, len = elements.length; i < len; i++)
try {
if (popupItem.contains(elements[i]))
return true;
if (elements[i].contains(popupItem))
return false;
}
catch (err) {
return false;
}
return false;
};
const isServer = (vNode) => typeof vNode.componentInstance !== "undefined" &&
vNode.componentInstance.$isServer;
export const bind = (el, binding, vNode) => {
if (!validate(binding))
return;
// Define Handler and cache it on the element
const handler = (event) => {
if (!vNode.context)
return;
// Some components may have related popup item, on which we shall prevent the click outside event handler.
// eslint-disable-next-line
const elements =
// eslint-disable-next-line
event.path ||
(event.composedPath ? event.composedPath() : []);
if (elements && elements.length > 0)
elements.unshift(event.target);
if (el.contains(event.target) ||
// eslint-disable-next-line
isPopup(vNode.context.popupItem, elements))
return;
if (el.$vueClickOutside)
el.$vueClickOutside.callback(event);
};
// Add Event Listeners
el.$vueClickOutside = {
handler,
callback: binding.value,
};
const clickHandler = "ontouchstart" in document.documentElement ? "touchstart" : "click";
if (!isServer(vNode))
document.addEventListener(clickHandler, handler);
};
export const update = (el, binding) => {
if (validate(binding) && el.$vueClickOutside)
el.$vueClickOutside.callback = binding.value;
};
export const unbind = (el, _binding, vNode) => {
// Remove Event Listeners
const clickHandler = "ontouchstart" in document.documentElement ? "touchstart" : "click";
if (!isServer(vNode) && el.$vueClickOutside)
document.removeEventListener(clickHandler, el.$vueClickOutside.handler);
delete el.$vueClickOutside;
};
export default {
bind,
update,
unbind,
};
//# sourceMappingURL=click-outside.js.map
\ No newline at end of file
{"version":3,"file":"click-outside.js","sourceRoot":"","sources":["click-outside.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
export default class Color {
type: "hex" | "rgb";
red: number;
green: number;
blue: number;
alpha: number;
constructor(type: "hex" | "rgb", red: number, green: number, blue: number, alpha?: number);
static fromHex(color: string): Color;
static fromRGB(color: string): Color;
static getColor(colorString: string): Color;
toString(): string;
adjust(item: "red" | "green" | "blue" | "alpha", amount: number): void;
darken(amount: number): Color;
lighten(amount: number): Color;
}
export default class Color {
constructor(type, red, green, blue, alpha = 1) {
this.type = type;
this.red = red;
this.green = green;
this.blue = blue;
this.alpha = alpha;
}
static fromHex(color) {
const parseHex = (colorString) => parseInt(colorString, 16);
const parseAlpha = (colorString, total) => Math.round((parseHex(colorString) * 100) / total) / 100;
if (color.length === 4)
return new Color("hex", parseHex(color[1]) * 17, parseHex(color[2]) * 17, parseHex(color[3]) * 17);
if (color.length === 5)
return new Color("hex", parseHex(color[1]) * 17, parseHex(color[2]) * 17, parseHex(color[3]) * 17, parseAlpha(color[4], 15));
if (color.length === 7)
return new Color("hex", parseHex(color.substring(1, 3)), parseHex(color.substring(3, 5)), parseHex(color.substring(5, 7)));
return new Color("hex", parseHex(color.substring(1, 3)), parseHex(color.substring(3, 5)), parseHex(color.substring(5, 7)), parseAlpha(color.substring(7, 9), 255));
}
// From RGB or RGBA
static fromRGB(color) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const RGBAPattern = /rgba\((.+)?,(.+)?,(.+)?,(.+)?\)/u;
// eslint-disable-next-line @typescript-eslint/naming-convention
const RGBPattern = /rgb\((.+)?,(.+)?,(.+)?\)/u;
const fromRGB = (colorString) => colorString.includes("%")
? (Number(colorString.trim().substring(0, colorString.trim().length - 1)) /
100) *
256 -
1
: Number(colorString.trim());
const rgbaResult = RGBAPattern.exec(color);
if (rgbaResult)
return new Color("rgb", fromRGB(rgbaResult[1]), fromRGB(rgbaResult[2]), fromRGB(rgbaResult[3]), Number(rgbaResult[4] || 1));
const rgbResult = RGBPattern.exec(color);
if (rgbResult)
return new Color("rgb", fromRGB(rgbResult[1]), fromRGB(rgbResult[2]), fromRGB(rgbResult[3]));
throw new Error(`Can not handle color: ${color}`);
}
static getColor(colorString) {
if (colorString.startsWith("#"))
return this.fromHex(colorString);
return this.fromRGB(colorString);
}
toString() {
if (this.type === "hex" && this.alpha === 1) {
const toHex = (color) => color < 10
? color.toString()
: color === 10
? "a"
: color === 11
? "b"
: color === 12
? "c"
: color === 13
? "d"
: color === 14
? "e"
: "f";
if (this.red % 17 === 0 && this.green % 17 === 0 && this.blue % 17 === 0)
return `#${toHex(this.red / 17)}${toHex(this.green / 17)}${toHex(this.blue / 17)}`;
const getHex = (color) => toHex((color - (color % 16)) / 16) + toHex(color % 16);
return `#${getHex(this.red)}${getHex(this.green)}${getHex(this.blue)}`;
}
return this.alpha === 1
? `rgb(${this.red},${this.green},${this.blue})`
: `rgba(${this.red},${this.green},${this.blue},${this.alpha})`;
}
adjust(item, amount) {
const result = Math.round(this[item] * amount);
if (item === "alpha")
this.alpha = result < 0 ? 0 : result > 1 ? 1 : result;
else
this[item] = result < 0 ? 0 : result > 255 ? 255 : result;
}
darken(amount) {
this.adjust("red", 1 - amount);
this.adjust("green", 1 - amount);
this.adjust("blue", 1 - amount);
return this;
}
lighten(amount) {
this.adjust("red", 1 + amount);
this.adjust("green", 1 + amount);
this.adjust("blue", 1 + amount);
return this;
}
}
//# sourceMappingURL=color.js.map
\ No newline at end of file
{"version":3,"file":"color.js","sourceRoot":"","sources":["color.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
/**
* Change DOM classes
*
* @param domClass DOM classlist
* @param insert class to insert
* @param remove class to remove
*/
export declare const changeClass: (domClass: DOMTokenList, insert: string[], remove: string[]) => void;
/**
* Change DOM classes
*
* @param domClass DOM classlist
* @param insert class to insert
* @param remove class to remove
*/
export const changeClass = (domClass, insert, remove) => {
const oldClasses = [];
domClass.remove(...remove);
domClass.forEach((classname) => {
oldClasses.push(classname);
});
domClass.value = "";
domClass.add(...insert, ...oldClasses);
};
//# sourceMappingURL=dom.js.map
\ No newline at end of file
{"version":3,"file":"dom.js","sourceRoot":"","sources":["dom.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CACzB,QAAsB,EACtB,MAAgB,EAChB,MAAgB,EACV,EAAE;IACR,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;QAC7B,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IACH,QAAQ,CAAC,KAAK,GAAG,EAAE,CAAC;IACpB,QAAQ,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,UAAU,CAAC,CAAC;AACzC,CAAC,CAAC"}
\ No newline at end of file
import type { EncryptOptions } from "../types";
export declare const getPathMatchedKeys: (encryptOptions: EncryptOptions | undefined, path: string) => string[];
export declare const getPathEncryptStatus: (encryptOptions: EncryptOptions | undefined, passwordConfig: Record<string, string>, path: string) => boolean;
export const getPathMatchedKeys = (encryptOptions, path) => encryptOptions && typeof encryptOptions.config === "object"
? Object.keys(encryptOptions.config)
.filter((key) => path.startsWith(key))
.sort((a, b) => b.length - a.length)
: [];
export const getPathEncryptStatus = (encryptOptions, passwordConfig, path) => {
const hitKeys = getPathMatchedKeys(encryptOptions, path);
if (hitKeys.length !== 0) {
const { config } = encryptOptions;
return !hitKeys.some((key) => {
const keyConfig = config[key];
const hitPasswords = typeof keyConfig === "string" ? [keyConfig] : keyConfig;
return hitPasswords.some((password) => passwordConfig[key] === password);
});
}
return false;
};
//# sourceMappingURL=encrypt.js.map
\ No newline at end of file
{"version":3,"file":"encrypt.js","sourceRoot":"","sources":["encrypt.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,cAA0C,EAC1C,IAAY,EACF,EAAE,CACZ,cAAc,IAAI,OAAO,cAAc,CAAC,MAAM,KAAK,QAAQ;IACzD,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC;SAC/B,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;SACrC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,CAAC,CAAC,EAAE,CAAC;AAET,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,cAA0C,EAC1C,cAAsC,EACtC,IAAY,EACH,EAAE;IACX,MAAM,OAAO,GAAG,kBAAkB,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;IAEzD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE;QACxB,MAAM,EAAE,MAAM,EAAE,GAAG,cAA0C,CAAC;QAE9D,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YAC3B,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,YAAY,GAChB,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAE1D,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,CAAC;QAC3E,CAAC,CAAC,CAAC;KACJ;IAED,OAAO,KAAK,CAAC;AACf,CAAC,CAAC"}
\ No newline at end of file
import type { PageHeader } from "@mr-hope/vuepress-types";
export interface SidebarHeader extends PageHeader {
children?: PageHeader[];
}
/** Group lower level headings under h2 children */
export declare const groupHeaders: (headers: PageHeader[]) => SidebarHeader[];
/** Group lower level headings under h2 children */
export const groupHeaders = (headers) => {
const headerscopy = headers.map((header) => (Object.assign({}, header)));
let lastH2;
// group other headings under h2 headings
headerscopy.forEach((header) => {
if (header.level === 2)
lastH2 = header;
else if (lastH2) {
if (!lastH2.children)
lastH2.children = [];
lastH2.children.push(header);
}
});
// filter only h2 headings
return headerscopy.filter((header) => header.level === 2);
};
//# sourceMappingURL=groupHeader.js.map
\ No newline at end of file
{"version":3,"file":"groupHeader.js","sourceRoot":"","sources":["groupHeader.ts"],"names":[],"mappings":"AAMA,mDAAmD;AACnD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAqB,EAAmB,EAAE;IACrE,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,mBAAM,MAAM,EAAG,CAAC,CAAC;IAC7D,IAAI,MAAqB,CAAC;IAE1B,yCAAyC;IACzC,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE;QAC7B,IAAI,MAAM,CAAC,KAAK,KAAK,CAAC;YAAE,MAAM,GAAG,MAAM,CAAC;aACnC,IAAI,MAAM,EAAE;YACf,IAAI,CAAC,MAAM,CAAC,QAAQ;gBAAE,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC;YAC3C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SAC9B;IACH,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC,CAAC"}
\ No newline at end of file
import type { HopeNavBarConfigItem } from "../types";
export interface NavBarConfigItem extends HopeNavBarConfigItem {
type: "link" | "links";
items: NavBarConfigItem[];
}
export declare const getNavLinkItem: (navbarLink: HopeNavBarConfigItem, beforeprefix?: string) => NavBarConfigItem;
export const getNavLinkItem = (navbarLink, beforeprefix = "") => {
var _a;
const prefix = beforeprefix + (navbarLink.prefix || "");
const navbarItem = Object.assign({}, navbarLink);
if (prefix) {
if (navbarItem.link !== undefined)
navbarItem.link = prefix + navbarItem.link;
delete navbarItem.prefix;
}
if ((_a = navbarItem.items) === null || _a === void 0 ? void 0 : _a.length)
Object.assign(navbarItem, {
type: "links",
items: navbarItem.items.map((item) => getNavLinkItem(item, prefix)),
});
else
navbarItem.type = "link";
return navbarItem;
};
//# sourceMappingURL=navbar.js.map
\ No newline at end of file
{"version":3,"file":"navbar.js","sourceRoot":"","sources":["navbar.ts"],"names":[],"mappings":"AAOA,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,UAAgC,EAChC,YAAY,GAAG,EAAE,EACC,EAAE;;IACpB,MAAM,MAAM,GAAG,YAAY,GAAG,CAAC,UAAU,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IAExD,MAAM,UAAU,qBACX,UAAU,CACd,CAAC;IAEF,IAAI,MAAM,EAAE;QACV,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS;YAC/B,UAAU,CAAC,IAAI,GAAG,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC;QAC7C,OAAO,UAAU,CAAC,MAAM,CAAC;KAC1B;IAED,IAAI,MAAA,UAAU,CAAC,KAAK,0CAAE,MAAM;QAC1B,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE;YACxB,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;SACpE,CAAC,CAAC;;QACA,UAAU,CAAC,IAAI,GAAG,MAAM,CAAC;IAE9B,OAAO,UAA8B,CAAC;AACxC,CAAC,CAAC"}
\ No newline at end of file
import type VueRouter from "vue-router";
import type { Route } from "vue-router";
/**
* @param url navigate link
* @param router router
* @param route current route
*/
export declare const navigate: (url: string, router: VueRouter, route: Route) => void;
/**
* @param url navigate link
* @param router router
* @param route current route
*/
export const navigate = (url, router, route) => {
if (url)
if (url.startsWith("/")) {
// Inner absolute path
if (route.path !== url)
void router.push(url);
}
else if (url.startsWith("http://") ||
url.startsWith("https://") ||
url.startsWith("mailto:")) {
// Outter url
if (window)
window.open(url);
}
else {
// Inner relative path
const base = route.path.slice(0, route.path.lastIndexOf("/"));
void router.push(`${base}/${encodeURI(url)}`);
}
};
//# sourceMappingURL=navigate.js.map
\ No newline at end of file
{"version":3,"file":"navigate.js","sourceRoot":"","sources":["navigate.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CACtB,GAAW,EACX,MAAiB,EACjB,KAAY,EACN,EAAE;IACR,IAAI,GAAG;QACL,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE;YACvB,sBAAsB;YACtB,IAAI,KAAK,CAAC,IAAI,KAAK,GAAG;gBAAE,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SAC/C;aAAM,IACL,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;YACzB,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC;YAC1B,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EACzB;YACA,aAAa;YACb,IAAI,MAAM;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;SAC9B;aAAM;YACL,sBAAsB;YACtB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAE9D,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SAC/C;AACL,CAAC,CAAC"}
\ No newline at end of file
import type { Route } from "vue-router";
export declare const hashRE: RegExp;
export declare const extRE: RegExp;
export declare const endingSlashRE: RegExp;
export declare const outboundRE: RegExp;
/** Remove hash and ext in a link */
export declare const normalize: (path: string) => string;
export declare const getHash: (path: string) => string | void;
/** Judge whether a path is external */
export declare const isExternal: (path: string) => boolean;
/** Judge whether a path is `mailto:` link */
export declare const isMailto: (path: string) => boolean;
/** Judge whether a path is `tel:` link */
export declare const isTel: (path: string) => boolean;
export declare const ensureExt: (path: string) => string;
export declare const ensureEndingSlash: (path: string) => string;
/** Judge whether a route match a link */
export declare const isActive: (route: Route, path: string) => boolean;
/**
* @param path links being resolved
* @param base deploy base
* @param append whether append directly
*/
export declare const resolvePath: (path: string, base: string, append?: boolean | undefined) => string;
export const hashRE = /#.*$/u;
export const extRE = /\.(md|html)$/u;
export const endingSlashRE = /\/$/u;
export const outboundRE = /^[a-z]+:/iu;
/** Remove hash and ext in a link */
export const normalize = (path) => decodeURI(path).replace(hashRE, "").replace(extRE, "");
export const getHash = (path) => {
const match = hashRE.exec(path);
if (match)
return match[0];
return "";
};
/** Judge whether a path is external */
export const isExternal = (path) => outboundRE.test(path);
/** Judge whether a path is `mailto:` link */
export const isMailto = (path) => path.startsWith("mailto:");
/** Judge whether a path is `tel:` link */
export const isTel = (path) => path.startsWith("tel:");
export const ensureExt = (path) => {
// do not resolve external links
if (isExternal(path))
return path;
const hashMatch = hashRE.exec(path);
const hash = hashMatch ? hashMatch[0] : "";
const normalized = normalize(path);
// do not resolve links ending with `/`
if (normalized.endsWith("/"))
return path;
// add `.html` ext
return `${normalized}.html${hash}`;
};
export const ensureEndingSlash = (path) => /(\.html|\/)$/u.test(path) ? path : `${path}/`;
/** Judge whether a route match a link */
export const isActive = (route, path) => {
const routeHash = decodeURIComponent(route.hash);
const linkHash = getHash(path);
// compare the hash only if the link has a hash
if (linkHash && routeHash !== linkHash)
return false;
const routePath = normalize(route.path);
const pagePath = normalize(path);
return routePath === pagePath;
};
/**
* @param path links being resolved
* @param base deploy base
* @param append whether append directly
*/
export const resolvePath = (path, base, append) => {
// do not resolve external links
if (isExternal(path))
return path;
const firstChar = path.charAt(0);
// do not resolve absolute links
if (firstChar === "/")
return path;
// if link is hash or query string, add with base
if (firstChar === "?" || firstChar === "#")
return `${base}${path}`;
// base links stack
const stack = base.split("/");
/*
* remove trailing segment if:
* - not appending
* - appending to trailing slash (last segment is empty)
*/
if (!append || !stack[stack.length - 1])
stack.pop();
// resolve relative path
const segments = path.replace(/^\//u, "").split("/");
for (let i = 0; i < segments.length; i++) {
const segment = segments[i];
if (segment === "..")
stack.pop();
else if (segment !== ".")
stack.push(segment);
}
// ensure leading slash
if (stack[0] !== "")
stack.unshift("");
return stack.join("/");
};
//# sourceMappingURL=path.js.map
\ No newline at end of file
{"version":3,"file":"path.js","sourceRoot":"","sources":["path.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
import type { PageComputed, SiteData } from "@mr-hope/vuepress-types";
import type { SidebarHeader } from "./groupHeader";
export type { SidebarHeader } from "./groupHeader";
export interface SidebarHeaderItem extends SidebarHeader {
type: "header";
basePath: string;
path: string;
}
export interface SidebarAutoItem {
type: "group";
/** Group title */
title: string;
/** Page Icon */
icon?: string;
/** Titles in page */
children: SidebarHeaderItem[];
collapsable: false;
path: "";
}
export declare const groupSidebarHeaders: (headers: import("@mr-hope/vuepress-types").PageHeader[]) => SidebarHeader[];
export interface SidebarExternalItem {
title?: string;
icon?: string;
type: "external";
path: string;
}
export interface SidebarPageItem extends PageComputed {
type: "page";
icon?: string;
path: string;
}
export interface SidebarGroupItem {
type: "group";
title: string;
/** @default true */
collapsable?: boolean;
/** @default 1 */
sidebarDepth?: number;
icon?: string;
prefix?: string;
children: SidebarItem[];
[props: string]: unknown;
}
export interface SidebarErrorItem {
type: "error";
path: string;
}
/** sidebarConfig merged with pageObject */
export declare const resolvePageforSidebar: (pages: PageComputed[], path: string) => SidebarPageItem | SidebarExternalItem | SidebarErrorItem;
export declare type SidebarItem = SidebarAutoItem | SidebarErrorItem | SidebarExternalItem | SidebarGroupItem | SidebarPageItem;
export declare const getSidebarItems: (page: PageComputed, site: SiteData, localePath: string) => SidebarItem[];
import { groupHeaders } from "./groupHeader";
import { ensureEndingSlash, ensureExt, isExternal, normalize, resolvePath, } from "./path";
export const groupSidebarHeaders = groupHeaders;
const resolveSidebarHeaders = (page) => {
const headers = page.headers ? groupSidebarHeaders(page.headers) : [];
return [
{
type: "group",
collapsable: false,
title: page.title,
icon: page.frontmatter.icon,
path: "",
children: headers.map((header) => (Object.assign(Object.assign({}, header), { type: "header", basePath: page.path, path: `${page.path}#${header.slug}`, children: header.children }))),
},
];
};
const findMatchingSidebarConfig = (regularPath, config) => {
// return directly as array-type config is the moest simple config
if (Array.isArray(config))
return {
base: "/",
config,
};
// find matching config
for (const base in config)
if (ensureEndingSlash(regularPath).startsWith(encodeURI(base)))
return {
base,
config: config[base],
};
console.warn(`${regularPath} do not have valid sidebar config`);
return false;
};
/** sidebarConfig merged with pageObject */
export const resolvePageforSidebar = (pages, path) => {
// if it is external link
if (isExternal(path))
return {
type: "external",
path,
};
const realPath = normalize(path);
// find matches in all pages
for (const page of pages)
if (normalize(page.regularPath) === realPath)
// return sidebarConfig merged with pageObject
return Object.assign(Object.assign({}, page), { type: "page", path: ensureExt(page.path) });
console.error(`Sidebar: "${realPath}" has no matching page`);
return { type: "error", path: realPath };
};
const resolve = (prefix, path, base) => resolvePath(`${prefix}${path}`, base);
/**
* @param sidebarConfigItem config item being resolved
* @param pages pages Object
* @param base sidebar base
*/
const resolveSidebarItem = (sidebarConfigItem, pages, base, prefix = "") => {
// resolve and return directly
if (typeof sidebarConfigItem === "string")
return resolvePageforSidebar(pages, resolve(prefix, sidebarConfigItem, base));
// custom title with format `['path', 'customTitle']`
if (Array.isArray(sidebarConfigItem))
return Object.assign(resolvePageforSidebar(pages, resolve(prefix, sidebarConfigItem[0], base)), { title: sidebarConfigItem[1] });
const children = sidebarConfigItem.children || [];
// item do not have children
if (children.length === 0 && sidebarConfigItem.path)
// cover title
return Object.assign(resolvePageforSidebar(pages, resolve(prefix, sidebarConfigItem.path, base)), { title: sidebarConfigItem.title });
// resolve children recursively then return
return Object.assign(Object.assign({}, sidebarConfigItem), { type: "group", path: sidebarConfigItem.path
? resolve(prefix, sidebarConfigItem.path, base)
: "", children: children.map((child) => resolveSidebarItem(child, pages, base, `${prefix}${sidebarConfigItem.prefix || ""}`)), collapsable: sidebarConfigItem.collapsable !== false });
};
export const getSidebarItems = (page, site, localePath) => {
const { themeConfig, pages } = site;
const localeConfig = localePath && themeConfig.locales
? themeConfig.locales[localePath] || themeConfig
: themeConfig;
const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar;
// auto generate sidebar through headings
if (page.frontmatter.sidebar === "auto" || sidebarConfig === "auto")
return resolveSidebarHeaders(page);
// sidebar is disabled
if (!sidebarConfig)
return [];
const result = findMatchingSidebarConfig(page.regularPath, sidebarConfig);
return result
? result.config.map((item) => resolveSidebarItem(item, pages, result.base))
: [];
};
//# sourceMappingURL=sidebar.js.map
\ No newline at end of file
{"version":3,"file":"sidebar.js","sourceRoot":"","sources":["sidebar.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
---
title: Welcome to the OP Stack
lang: en-US
---
**The OP Stack is the standardized, shared, and open-source development stack that powers Optimism, maintained by the Optimism Collective.**
::: tip Staying up to date
[Stay up to date on the Superchain and the OP Stack by subscribing to the Optimism newsletter](https://optimism.us6.list-manage.com/subscribe/post?u=9727fa8bec4011400e57cafcb&id=ca91042234&f_id=002a19e3f0).
:::
The OP Stack consists of the many different software components managed and maintained by the Optimism Collective that, together, form the backbone of Optimism.
The OP Stack is built as a public good for the Ethereum and Optimism ecosystems.
## The OP Stack powers Optimism
The OP Stack is the set of software that powers Optimism — currently in the form of the software behind Optimism Mainnet and eventually in the form of the Optimism Superchain and its governance.
With the advent of the Superchain concept, it has become increasingly important for Optimism to easily support the secure creation of new chains that can interoperate within the proposed Superchain ecosystem.
As a result, the OP Stack is primarily focused around the creation of a shared, high-quality, and fully open-source system for creating new L2 blockchains.
By coordinating on shared standards, the Optimism Collective can avoid rebuilding the same software in silos repeatedly.
Although the OP Stack today significantly simplifies the process of creating L2 blockchains, it’s important to note that this does not fundamentally define what the OP Stack **is**.
The OP Stack is *all* of the software that powers Optimism.
As Optimism evolves, so will the OP Stack.
**The OP Stack can be thought of as software components that either help define a specific layer of the Optimism ecosystem or fill a role as a module within an existing layer.**
Although the current heart of the OP Stack is infrastructure for running L2 blockchains, the OP Stack theoretically extends to layers on top of the underlying blockchain including tools like block explorers, message passing mechanisms, governance systems, and more.
Layers are generally more tightly defined towards the bottom of the stack (like the Data Availability Layer) but become more loosely defined towards the top of the stack (like the Governance Layer).
## The OP Stack today
Optimism Bedrock is the current iteration of the OP Stack.
The Bedrock release provides the tools for launching a production-quality Optimistic Rollup blockchain.
At this point in time, the APIs for the different layers of the OP Stack are still tightly coupled to this Rollup configuration of the stack.
If you'd like to learn more about the current state of the OP Stack, check out [the page describing the Bedrock release](/docs/releases/bedrock/README.md).
The OP Stack of today was built to support [the Optimism Superchain](./docs/understand/explainer.md), a proposed network of L2s that share security, communication layers, and a common development stack (the OP Stack itself).
The Bedrock release of the OP Stack makes it easy to spin up an L2 that will be compatible with the Superchain when it launches.
If you'd like to launch a Superchain-ready L2, check out our guide for running a chain based on the Bedrock release of the OP Stack.
It is possible to modify components of the OP Stack to build novel L2 systems.
If you're interested in experimenting with the OP Stack, check out [the OP Stack Hacks section of this site](/docs/build/hacks.md).
Please note that, as of the Bedrock release, the OP Stack is *not* designed to support these modifications and you will very much be *hacking* on the codebase.
As a result, **you should, for the moment, expect limited (if any) developer support for OP Stack Hacks.**
OP Stack Hacks will likely make your chain incompatible with the Optimism Superchain.
Have fun, but at your own risk and **stick to the Bedrock release if you're looking to join the Superchain!**
## The OP Stack tomorrow
The OP Stack is an evolving concept.
As Optimism grows, so will the OP Stack.
Today, the Bedrock Release of the OP Stack simplifies the process of deploying new L2 Rollups.
As work on the stack continues, it should become easier to plug in and configure different modules.
As the Superchain (link) begins to take shape, the OP Stack can evolve alongside it, to include the message-passing infrastructure that allows different chains to interoperate seamlessly.
At the end of the day, the OP Stack becomes what Optimism needs.
## Dive Deeper into the OP Stack
Ready to dive into the world of the OP Stack?
- If you’re interested in learning more about the current release of the OP Stack, check out the Bedrock Release page.
- If you’re interested in understanding the OP Stack in more depth, start with the [Design Principles](/docs/understand/design-principles.md) and [Landscape Overview](/docs/understand/landscape.md).
- If you're excited to join the Superchain, launch your first Superchain-ready L2 with our [Getting Started guide](/docs/build/getting-started.md) or dive directly into the OP Stack codebase to learn more.
The OP Stack is the next frontier for Ethereum. You’re already here, so what are you waiting for?
---
title: OP Stack Docs
lang: en-US
---
[Click here](..)
---
title: Building with the OP Stack
lang: en-US
---
The OP Stack is the decentralized development stack that powers Optimism. The current release of the OP Stack (named “Bedrock”) was designed to facilitate the creation of L2 blockchains that can interoperate with the proposed [Optimism Superchain](https://app.optimism.io/superchain/). The Superchain is a proposed network of rollups that share a security model, communication protocols, and a common development stack (the OP Stack itself). **The best way to get started building with the OP Stack today is to launch your own Bedrock-based Rollup by following our guide to [Running a Bedrock Rollup](./getting-started.md)**. Bedrock Rollups will be compatible with the Superchain.
You can also experiment with the OP Stack by making modifications to its various components. The OP Stack, as of the Bedrock release, is not explicitly designed to handle any significant modifications out of the box. By modifying the components of the OP Stack, you will likely no longer be compatible with the Superchain. Please also note that there is limited developer support available for modifications to the OP Stack. Have fun, but modify the stack at your own risk!
---
title: Configuration
lang: en-US
---
The OP Stack is a flexible platform with various configuration values that you can tweak to fit your specific needs. If you’re looking to fine-tune your deployment, look no further.
::: warning 🚧 Work in Progress
OP Stack configuration is an active work in progress and will likely evolve significantly as time goes on. If something isn’t working about your configuration, check back with this page to see if anything has changed.
:::
## New Blockchain Configuration
New OP Stack blockchains are currently configured with a JSON file inside the Optimism repository. The file is `<optimism repository>/packages/contracts-bedrock/deploy-config/<chain name>.json`. For example, [this is the configuration file for the tutorial blockchain](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/deploy-config/getting-started.json).
### Admin accounts
| Key | Type | Description | Default / Recommended value |
| --- | --- | --- | --- |
| `finalSystemOwner` | L1 Address | Address that will own all ownable contracts on L1 once the deployment is finished, including the `ProxyAdmin` contract. | It is recommended to have a single admin account to retain a common security model. |
| `controller` | L1 Address | Address that will own the `SystemDictator` contract and can therefore control the flow of the deployment or upgrade. | It is recommended to have a single admin account to retain a common security model. |
| `proxyAdminOwner` | L2 Address | Address that will own the `ProxyAdmin` contract on L2. The L2 `ProxyAdmin` contract owns all of the `Proxy` contracts for every predeployed contract in the range `0x42...0000` to `0x42..2048`. This makes predeployed contracts easily upgradeable. | It is recommended to have a single admin account to retain a common security model. |
### Fee recipients
| Key | Type | Description | Default value |
| --- | --- | --- | --- |
| `baseFeeVaultRecipient` | L1 or L2 Address | Address that the base fees from all transactions on the L2 can be withdrawn to. | It is recommended to have a single admin account to retain a common security model. |
| `l1FeeVaultRecipient` | L1 or L2 Address | Address that the L1 data fees from all transactions on the L2 can be withdrawn to. | It is recommended to have a single admin account to retain a common security model. |
| `sequencerFeeVaultRecipient` | L1 or L2 Address | Address that the tip fees from all transactions on the L2 can be withdrawn to. | It is recommended to have a single admin account to retain a common security model. |
### Minimum Fee Withdrawal Amounts
| Key | Type | Description | Default value |
| --- | --- | --- | --- |
| `baseFeeVaultMinimumWithdrawalAmount` | Number in wei | The minimum amount of funds the `BaseFeeVault` contract must have for a fee withdrawal. | 10 ether |
| `l1FeeVaultMinimumWithdrawalAmount` | Number in wei | The minimum amount of funds the `L1FeeVault` contract must have for a fee withdrawal. | 10 ether |
| `sequencerFeeVaultWithdrawalAmount` | Number in wei | The minimum amount of funds the `SequencerFeeVault` contract must have for a fee withdrawal. | 10 ether |
### Withdrawal Network
| Key | Type | Description | Default value |
| --- | --- | --- | --- |
| `baseFeeVaultWithdrawalNetwork` | Number representing network enum | A value of `0` will withdraw funds to the recient address on L1 and a value of `1` will withdraw funds to the recient address on L2. |
| `l1FeeVaultWithdrawalNetwork` | Number representing network enum | A value of `0` will withdraw funds to the recient address on L1 and a value of `1` will withdraw funds to the recient address on L2. |
| `sequencerFeeVaultWithdrawalNetwork` | Number representing network enum | A value of `0` will withdraw funds to the recient address on L1 and a value of `1` will withdraw funds to the recient address on L2. |
### Misc.
| Key | Type | Description | Default value |
| --- | --- | --- | --- |
| `numDeployConfirmations` | Number of blocks | Number of confirmations to wait when deploying smart contracts to L1. | 1 |
| `l1StartingBlockTag` | Block hash | Block tag for the L1 block where the L2 chain will begin syncing from. Generally recommended to use a finalized block to avoid issues with reorgs. | |
| `l1ChainID` | Number | Chain ID of the L1 chain. | 1 for L1 Ethereum mainnet, <br> 5 for the Goerli test network. <br> [See here for other blockchains](https://chainlist.org/?testnets=true). |
| `l2ChainID` | Number | Chain ID of the L2 chain. | 42069 |
### Blocks
These fields apply to L2 blocks: Their timing, when do they need to be written to L1, and how they get written.
| Key | Type | Description | Default value |
| --- | --- | --- | --- |
| `l2BlockTime` | Number of seconds | Number of seconds between each L2 block. Must be <= L1 block time (12 on mainnet and Goerli) | 2 |
| `maxSequencerDrift` | Number of seconds | How far the L2 timestamp can differ from the actual L1 timestamp | 600 (10 minutes) |
| `sequencerWindowSize` | Number of blocks | Maximum number of L1 blocks that a Sequencer can wait to incorporate the information in a specific L1 block. For example, if the window is `10` then the information in L1 block `n` must be incorporated by L1 block `n+10`. | 3600 (12 hours) |
| `channelTimeout` | Number of blocks | Maximum number of L1 blocks that a transaction channel frame can be considered valid. A transaction channel frame is a chunk of a compressed batch of transactions. After the timeout, the frame is dropped. | 300 (1 hour) |
| `p2pSequencerAddress` | L1 Address | Address of the key that the Sequencer uses to sign blocks on the p2p network. | Sequencer, an address for which you own the private key |
| `batchInboxAddress` | L1 Address | Address that Sequencer transaction batches are sent to on L1. | 0xff00…0042069 |
| `batchSenderAddress` | L1 Address | Address of the account that nodes will filter for when searching for Sequencer transaction batches being sent to the `batchInboxAddress`. Can be updated later via the `SystemConfig` contract on L1. | Batcher, an address for which you own the private key |
### Proposal fields
These fields apply to output root proposals.
| Key | Type | Description | Default value |
| --- | --- | --- | --- |
| `l2OutputOracleStartingBlockNumber` | Number | Block number of the first OP Stack block. Typically this should be zero, but this may be non-zero for networks that have been upgraded from a legacy system (like Optimism Mainnet). Will be removed with the addition of permissionless proposals. | 0 |
| `l2OutputOracleStartingTimestamp` | Number | Timestamp of the first OP Stack block. This MUST be the timestamp corresponding to the block defined by the `l1StartingBlockTag`. Will be removed with the addition of permissionless proposals. | |
| `l2OutputOracleSubmissionInterval` | Number of blocks | Number of blocks between proposals to the `L2OutputOracle`. Will be removed with the addition of permissionless proposals. | 120 (24 minutes) |
| `finalizationPeriodSeconds` | Number of seconds | Number of seconds that a proposal must be available to challenge before it is considered finalized by the `OptimismPortal` contract. | We recommend 12 on test networks, seven days on production ones |
| `l2OutputOracleProposer` | L1 Address | Address that is allowed to submit output proposals to the `L2OutputOracle` contract. Will be removed when we have permissionless proposals. | |
| `l2OutputOracleChallenger` | L1 Address | Address that is allowed to challenge output proposals submitted to the `L2OutputOracle`. Will be removed when we have permissionless challenges. | It is recommended to have a single admin account to retain a common security model. |
### L1 data fee
These fields apply to the cost of the [L1 data fee](https://community.optimism.io/docs/developers/build/transaction-fees/#the-l1-data-fee) for L2 transactions.
| Key | Type | Description | Default value |
| --- | --- | --- | --- |
| `gasPriceOracleOverhead` | Number | Fixed L1 gas overhead per transaction. Default value will likely be adjusted with more information from the Optimism Goerli deployment. | 2100 |
| `gasPriceOracleScalar` | Number | Dynamic L1 gas overhead per transaction, given in 6 decimals. Default value of 1000000 implies a dynamic gas overhead of exactly 1x (no overhead). | 1000000 |
### EIP 1559 gas algorithm
These fields apply to [the EIP 1559 algorithm](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md) used for the [L2 execution costs](https://community.optimism.io/docs/developers/build/transaction-fees/#the-l2-execution-fee) of transactions on the blockchain.
| Key | Type | Description | Default value | Value on L1 Ethereum |
| --- | --- | --- | --- | --- |
| `eip1559Denominator` | Number | Denominator used for the [EIP1559 gas pricing mechanism on L2](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md). A larger denominator decreases the amount by which the base fee can change in a single block. | 50 | 8 |
| `eip1559Elasticity` | Number | Elasticity for the [EIP1559 gas pricing mechanism on L2](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1559.md). A larger elasticity increases the maximum allowable gas limit per block. | 10 | 2 |
| `l2GenesisBlockGasLimit` | String | Initial block gas limit, represented as a hex string. Default is 25m, implying a 2.5m target when combined with a 10x elasticity. | 0x17D7840 | |
| `l2GenesisBlockBaseFeePerGas` | String | Initial base fee, used to avoid an unstable EIP1559 calculation out of the gate. Initial value is 1 gwei. | 0x3b9aca00 | |
### Governance token
The governance token is a side-effect of use of the OP Stack in the Optimism Mainnet network. It may not be included by default in future releases.
| Key | Type | Description | Default value |
| --- | --- | --- | --- |
| `governanceTokenOwner` | L2 Address | Address that will own the token contract deployed by default to every OP Stack based chain. | |
| `governanceTokenSymbol` | String | Symbol for the token deployed by default to each OP Stack chain. | OP |
| `governanceTokenName` | String | Name for the token deployed by default to each OP Stack chain. | Optimism |
---
title: Data Availability Hacks
lang: en-US
---
::: warning 🚧 OP Stack Hacks are explicitly things that you can do with the OP Stack that are *not* currently intended for production use
OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.
:::
## Overview
The Data Availability Layer is responsible for the *ordering* and *storage* of the raw input data that forms the backbone of an OP Stack based chain (transactions, state roots, calls from other blockchains, etc.). You can conceptually think of this as an array of inputs — the ordering of this array should remain stable and the contents of this array should remain available. Unstable ordering of inputs will lead to reorgs of the OP Stack chain, while unavailable inputs will cause the OP Stack chain to halt entirely.
## Default
The default Data Availability Layer module for an OP Stack chain is the Ethereum DA module. When using the Ethereum DA module, all raw input data is expected to be found on Ethereum. Any data that is accessible on Ethereum can be queried when using this module, including calldata, events, and other block data.
## Security
OP Stack based chains are functions of the raw input data found on the Data Availability Layer module(s) used. If a required piece of data is not available, nodes will not be able to properly sync the chain. This also means that these nodes will not be able to dispute any invalid state proposals made to a Settlement Layer module. An OP Stack based chain cannot be safer than the Data Availability module.
You should be careful to understand the security properties of any Data Availability module(s) that you use. The standard Ethereum DA module generally provides the best security guarantees at the cost of higher transaction fees. Alternative DA modules may be appropriate depending on your particular use-case and risk tolerance.
## Modding
### Alternative EVM DA
A simple modification is to use an EVM-based blockchain other than Ethereum as the Data Availability Layer. Doing so simply requires using an L1 RPC other than Ethereum.
### EVM-Ordered Alternative DA
A more involved modification to the Data Availability Layer is an "EVM-Ordered" Alternative DA module. This involves using an EVM-based chain to maintain the *ordering* of transaction data while using a different data storage system to host the underlying data. Generally, ordering is maintained by publishing hashes of the data to the EVM-based chain while publishing the preimages to those hashes to the alternative data source.
An EVM-Ordered Alternative DA module significantly reduces costs by only publishing hashes and not full input data to the EVM chain. Using an EVM chain for ordering also reduces the number of changes that must be made to the standard Rollup configuration to achieve this result.
An example of an EVM-Ordered Alternative DA module can be found within [this modification to the OP Stack](https://github.com/celestiaorg/optimism/pull/3) that uses the Celestia blockchain as a third-party data availability provider.
### Non-EVM DA
A non-EVM DA module uses a chain not based on the EVM to manage both the ordering and storage of raw input data. Such a modification would require relatively significant modifications to the [derivation portion](https://github.com/ethereum-optimism/optimism/tree/129032f15b76b0d2a940443a39433de931a97a44/op-node/rollup/derive) of the `op-node`. No such fully-independent DA modules have been developed yet — be the first!
### Multiple DA
It is possible to use multiple Data Availability Layer modules at the same time. For instance, one could source data from two EVM-based chains simultaneously in order to form a bridge between the two chains. When using multiple Data Availability Layer modules, it is imperative to establish a global ordering between the two chains. One option for establishing this ordering is to use the timestamps of blocks from each chain.
Like a non-EVM DA module, a system with multiple Data Availability modules would need to make significant modifications to the [derivation portion](https://github.com/ethereum-optimism/optimism/tree/129032f15b76b0d2a940443a39433de931a97a44/op-node/rollup/derive) of the `op-node`. No such projects have been constructed yet.
\ No newline at end of file
---
title: Derivation Hacks
lang: en-US
---
::: warning 🚧 OP Stack Hacks are explicitly things that you can do with the OP Stack that are *not* currently intended for production use
OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.
:::
## Overview
The Derivation layer is responsible for parsing the raw inputs from the Data Availability layer and converting them into [Engine API](https://github.com/ethereum/execution-apis/tree/main/src/engine) payloads to be sent to the Execution layer. The Derivation Layer is generally tightly coupled to the Data Availability layer because it must understand both the APIs for the Data Availability layer module(s) of choice and the format of the raw data published to the chosen module(s).
## Default
The default Derivation layer module is the Rollup module. This module derives transactions from three sources: Sequencer transactions, user deposits, and L1 blocks. The Rollup module also enforces certain ordering properties that, for example, guarantee that user deposits are always included in the L2 chain within a certain configurable amount of time.
## Security
Modifying the Derivation layer can have unintended consequences. For example, removing or extending the time window in which user deposits must be included can allow a Sequencer to censor the L2 chain. Because of the flexibility of the Derivation layer, the exact impact of any change is likely to be unique to the specifics of the change. The negative impacts of any modifications should be carefully considered on a case-by-case basis.
## Modding
### EVM Event-Triggered Transactions
The default Rollup configuration of the OP Stack includes “deposited” transactions that are triggered whenever a specific event is emitted by the `OptimismPortal` contract on L1. Using the same principle, an OP Stack chain can derive transactions from events emitted by *any* contract on an EVM-based DA. Refer to [attributes.go](https://github.com/ethereum-optimism/optimism/blob/e468b66efedc5f47f4e04dc1acc803d4db2ce383/op-node/rollup/derive/attributes.go#L70) to understand how deposited transactions are derived and how custom transactions can be created.
### EVM Block-Triggered Transactions
Like with events, transactions on an OP Stack chain can be triggered whenever a new block is published on an EVM-based DA. The default Rollup configuration of the OP Stack already includes a block-triggered transaction in the form of [the “L1 info” transaction](https://github.com/ethereum-optimism/optimism/blob/e468b66efedc5f47f4e04dc1acc803d4db2ce383/op-node/rollup/derive/attributes.go#L103) that relays information like the latest block hash, timestamp, and base fee into L2. The Getting Started guide demonstrates the addition of a new block-triggered transaction in the form of a new transaction that reports the amount of gas burned via the base fee on L1.
### And much, much more…
The Derivation layer is one of the most flexible layers of the stack. Transactions can be generated from all sorts of raw input data and can be triggered from all sorts of conditions. You can derive transactions from any piece of data that can be found in the Data Availability layer modules!
[Tutorial: Adding attributes to the derivation function](./tutorials/add-attr.md).
\ No newline at end of file
---
title: Execution Hacks
lang: en-US
---
::: warning 🚧 OP Stack Hacks are explicitly things that you can do with the OP Stack that are *not* currently intended for production use
OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.
:::
## Overview
The Execution Layer is responsible for defining the format of state and the state transition function on L2. It is expected to trigger the state transition function when it receives a payload via the [Engine API](https://github.com/ethereum/execution-apis/tree/main/src/engine). Although the default Execution Layer module is the EVM, you can replace the EVM with any alternative VM as long as it sits behind the Engine API.
## Default
The default Execution Layer module is the Rollup EVM module. The Rollup EVM module utilizes a very lightly modified EVM that adds support for transactions that are triggered by smart contracts on L1 and introduces an L1 data fee to each transaction that accounts for the cost of publishing user transactions to L1. You can find the full set of differences between the standard EVM and the Rollup EVM [on this page](https://op-geth.optimism.io/).
## Security
As with modifications to the Derivation Layer, modifications to the Execution Layer can have unintended consequences. For instance, modifications to the EVM may break existing tooling or may open the door to denial of service attacks. Consider the impact of each modification carefully on a case-by-case basis.
## Modding
### EVM Tweaks
The default Execution Layer module is the EVM. It’s possible to modify the EVM in many different ways like adding new precompiles or inserting predeployed smart contracts into the genesis state. Precompiles can help make common smart contract operations cheaper and can therefore further reduce the cost of execution for your specific use-case. These modifications should be made directly to [the execution client](https://github.com/ethereum-optimism/op-geth).
It’s also possible to create alternative execution client implementations to improve the security properties of your chain. Note that if you modify the EVM, you must apply the same modifications to every execution client that you would like to support.
### Alternative VMs
The OP Stack allows you to replace the EVM with *any* state transition function, as long as the transition can be triggered via the Engine API. This has, for example, been used to implement an OP Stack chain that runs a GameBoy emulator rather than the EVM.
[Tutorial: Adding a precompile](./tutorials/new-precomp.md).
\ No newline at end of file
---
title: Explorer and Indexer
lang: en-US
---
The next step is to be able to see what is actually happening in your blockchain.
One easy way to do this is to use [Blockscout](https://www.blockscout.com/).
## Prerequisites
### Archive mode
Blockscout expects to interact with an Ethereum execution client in [archive mode](https://www.alchemy.com/overviews/archive-nodes#archive-nodes).
If your `op-geth` is running in full mode, you can create a separate archive node.
To do so, follow the [directions to add a node](./getting-started.md#adding-nodes), but in the command you use to start `op-geth` replace:
```sh
--gcmode=full \
```
with
```sh
--gcmode=archive \
```
### Docker
The easiest way to run Blockscout is to use Docker.
Download and install [Docker engine](https://docs.docker.com/engine/install/#server).
## Installation and configuration
1. Clone the Blockscout repository.
```sh
cd ~
git clone https://github.com/blockscout/blockscout.git
cd blockscout/docker-compose
```
1. Depending on the version of Docker you have, there may be an issue with the environment path.
Run this command to fix it:
```sh
ln -s `pwd`/envs ..
```
1. If `op-geth` in archive mode runs on a different computer or a port that isn't 8545, edit `docker-compose-no-build-geth.yml` to set `ETHEREUM_JSONRPC_HTTP_URL` to the correct URL.
1. Start Blockscout
```sh
docker compose -f docker-compose-no-build-geth.yml up
```
## Usage
After the docker containers start, browse to http:// < *computer running Blockscout* > :4000 to view the user interface.
You can also use the [API](https://docs.blockscout.com/for-users/api)
### GraphQL
Blockscout's API includes [GraphQL](https://graphql.org/) support under `/graphiql`.
For example, this query looks at addresses.
```
query {
addresses(hashes:[
"0xcB69A90Aa5311e0e9141a66212489bAfb48b9340",
"0xC2dfA7205088179A8644b9fDCecD6d9bED854Cfe"])
```
GraphQL queries start with a top level entity (or entities).
In this case, our [top level query](https://docs.blockscout.com/for-users/api/graphql#queries) is for multiple addresses.
Note that you can only query on fields that are indexed.
For example, here we query on the addresses.
However, we couldn't query on `contractCode` or `fetchedCoinBalance`.
```
{
hash
contractCode
fetchedCoinBalance
```
The fields above are fetched from the address table.
```
transactions(first:5) {
```
We can also fetch the transactions that include the address (either as source or destination).
The API does not let us fetch an unlimited number of transactions, so here we ask for the first 5.
```
edges {
node {
```
Because this is a [graph](https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)), the entities that connect two types, for example addresses and transactions, are called `edges`.
At the other end of each edge there is a transaction, which is a separate `node`.
```
hash
fromAddressHash
toAddressHash
input
}
```
These are the fields we read for each transaction.
```
}
}
}
}
```
Finally, close all the brackets.
\ No newline at end of file
---
title: Featured Hacks
lang: en-US
---
::: warning 🚧 OP Stack Hacks are explicitly things that you can do with the OP Stack that are *not* currently intended for production use
OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.
:::
## Overview
Featured Hacks is a compilation of some of the cool stuff people are building on top of the OP Stack!
## OPCraft
### Author
[Lattice](https://lattice.xyz/)
### Description
OPCraft was an OP Stack chain that ran a modified EVM as the backend for a fully onchain 3D voxel game built with [MUD](https://mud.dev/).
### OP Stack Configuration
- Data Availability: Ethereum DA (Goerli)
- Sequencer: Single Sequencer
- Derivation: Standard Rollup
- Execution: Modified Rollup EVM
### Links
- [Announcing OPCraft: an Autonomous World built on the OP Stack](https://dev.optimism.io/opcraft-autonomous-world/)
- [OPCraft Explorer](https://opcraft.mud.dev/)
- [OPCraft on GitHub](https://github.com/latticexyz/opcraft)
- [MUD](https://mud.dev/)
## Ticking Optimism
### Author
[@therealbytes](https://twitter.com/therealbytes)
### Description
Ticking Optimism is a proof-of-concept implementation of an OP Stack chain that calls a `tick` function every block. By using the OP Stack, Ticking Optimism avoids the need for off-chain infrastructure to execute a function on a regular basis. Ticking Conway is a system that uses Ticking Optimism to build [Conway’s Game of Life](https://conwaylife.com/) onchain.
### OP Stack Configuration
- Data Availability: Ethereum DA (any)
- Sequencer: Single Sequencer
- Derivation: Standard Rollup with custom `tick` function
- Execution: Rollup EVM
### Links
- [Ticking Optimism on GitHub](https://github.com/therealbytes/ticking-optimism)
- [Ticking Conway on GitHub](https://github.com/therealbytes/ticking-conway)
\ No newline at end of file
---
title: Getting Started
lang: en-US
---
## Overview
Hello! This Getting Started guide is meant to help you kick off your OP Stack journey by taking you through the process of spinning up your very own OP Stack chain on the Ethereum Goerli testnet. You can use this chain to perform tests and prepare for the superchain, or you can modify it to adapt it to your own needs (which may make it incompatible with the superchain in the future).
## Know before you go
Before we kick off, note that this is a relatively long tutorial! You should prepare to set aside an hour or two to get everything running. Here’s an itemized list of what we’re about to do:
1. Install dependencies
2. Build the source code
3. Generate and fund accounts and private keys
4. Configure your network
5. Deploy the L1 contracts
6. Initialize op-geth
7. Run op-geth
8. Run op-node
9. Get some Goerli ETH on your L2
10. Send some test transactions
11. Celebrate!
## Prerequisites
You’ll need the following software installed to follow this tutorial:
- [Git](https://git-scm.com/)
- [Go](https://go.dev/)
- [Node](https://nodejs.org/en/)
- [Pnpm](https://classic.yarnpkg.com/lang/en/docs/install/)
- [Foundry](https://github.com/foundry-rs/foundry#installation)
- [Make](https://linux.die.net/man/1/make)
- [jq](https://github.com/jqlang/jq)
- [direnv](https://direnv.net)
This tutorial was checked on:
| Software | Version | Installation command(s) |
| -------- | ---------- | - |
| Ubuntu | 20.04 LTS | |
| git, curl, jq, and make | OS default | `sudo apt install -y git curl make jq` |
| Go | 1.20 | `sudo apt update` <br> `wget https://go.dev/dl/go1.20.linux-amd64.tar.gz` <br> `tar xvzf go1.20.linux-amd64.tar.gz` <br> `sudo cp go/bin/go /usr/bin/go` <br> `sudo mv go /usr/lib` <br> `echo export GOROOT=/usr/lib/go >> ~/.bashrc`
| Node | 16.19.0 | `curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -` <br> `sudo apt-get install -y nodejs npm`
| pnpm | 8.5.6 | `sudo npm install -g pnpm`
| Foundry | 0.2.0 | `yarn install:foundry`
## Build the Source Code
We’re going to be spinning up an EVM Rollup from the OP Stack source code. You could use docker images, but this way we keep the option to modify component behavior if you need to do so. The OP Stack source code is split between two repositories, the [Optimism Monorepo](https://github.com/ethereum-optimism/optimism) and the [`op-geth`](https://github.com/ethereum-optimism/op-geth) repository.
### Build the Optimism Monorepo
1. Clone the [Optimism Monorepo](https://github.com/ethereum-optimism/optimism).
```bash
cd ~
git clone https://github.com/ethereum-optimism/optimism.git
```
1. Enter the Optimism Monorepo.
```bash
cd optimism
```
1. Install required modules. This is a slow process, while it is running you can already start building `op-geth`, as shown below.
```bash
pnpm install
```
1. Build the various packages inside of the Optimism Monorepo.
```bash
make op-node op-batcher op-proposer
pnpm build
```
### Build op-geth
1. Clone [`op-geth`](https://github.com/ethereum-optimism/op-geth):
```bash
cd ~
git clone https://github.com/ethereum-optimism/op-geth.git
```
1. Enter `op-geth`:
```bash
cd op-geth
```
1. Build `op-geth`:
```bash
make geth
```
## Get access to a Goerli node
Since we’re deploying our OP Stack chain to Goerli, you’ll need to have access to a Goerli L1 node. You can either use a node provider like [Alchemy](https://www.alchemy.com/) (easier) or [run your own Goerli node](https://notes.ethereum.org/@launchpad/goerli) (harder).
## Generate some keys
You’ll need four accounts and their private keys when setting up the chain:
- The `Admin` account which has the ability to upgrade contracts.
- The `Batcher` account which publishes Sequencer transaction data to L1.
- The `Proposer` account which publishes L2 transaction results to L1.
- The `Sequencer` account which signs blocks on the p2p network.
You can generate all of these keys with the `rekey` tool in the `contracts-bedrock` package.
1. Enter the Optimism Monorepo:
```bash
cd optimism
```
1. Move into the `contracts-bedrock` package:
```bash
cd packages/contracts-bedrock
```
1. Use `cast wallet` to generate new accounts
```bash
echo "Admin:"
cast wallet new
echo "Proposer:"
cast wallet new
echo "Batcher:"
cast wallet new
echo "Sequencer:"
cast wallet new
```
You should get an output like the following:
```
Admin:
Successfully created new keypair.
Address: 0x9f92bdF0db69264462FC305913960Edfcc7a7c7F
Private key: 0x30e66956e1a12b81f0f2cfb982286b2f566eb73649833831d9f80b12f8fa183c
Proposer:
Successfully created new keypair.
Address: 0x31dE9B6473fc47af36ec23878bA34824B9F4AB30
Private key: 0x8bd1c8dfffef880f8f9ab8162f97ccd119c1aac28fe00dacf919459f88e0f37d
Batcher:
Successfully created new keypair.
Address: 0x6A3DC843843139f17Fcf04C057bb536A421DC9c6
Private key: 0x3ce44144b7fde797a28f4e47b210a4d42c3a3b642e538b54458cba2740db5ac2
Sequencer:
Successfully created new keypair.
Address: 0x98C6cadB1fe77aBB7bD968fC3E9b206111e72848
Private key: 0x3f4241229bb6f155140d98e0f5dd2aad7ae983f5af5d61555d05eb8e5d9514db
```
Save these accounts and their respective private keys somewhere, you’ll need them later. Fund the `Admin` address with a small amount of Goerli ETH as we’ll use that account to deploy our smart contracts. You’ll also need to fund the `Proposer` and `Batcher` address — note that the `Batcher` burns through the most ETH because it publishes transaction data to L1.
Recommended funding amounts are as follows:
- `Admin` — 2 ETH
- `Proposer` — 5 ETH
- `Batcher` — 10 ETH
::: danger Not for production deployments
The `cast wallet new` tool is *not* designed for production deployments. If you are deploying an OP Stack based chain into production, you should likely be using a combination of hardware security modules and hardware wallets.
:::
## Configure your network
Once you’ve built both repositories, you’ll need head back to the Optimism Monorepo to set up the configuration for your chain. Currently, chain configuration lives inside of the [`contracts-bedrock`](https://github.com/ethereum-optimism/optimism/tree/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock) package.
1. Enter the Optimism Monorepo:
```bash
cd ~/optimism
```
1. Move into the `contracts-bedrock` package:
```bash
cd packages/contracts-bedrock
```
1. Inside of `contracts-bedrock`, copy the environment file
```sh
cp .envrc.example .envrc
```
1. Fill out the environment variables inside of that file:
- `ETH_RPC_URL` — URL for your L1 node.
- `PRIVATE_KEY` — Private key of the `Admin` account.
- `DEPLOYMENT_CONTEXT` - Name of the network, should be "getting-started"
1. Pull the environment variables into context using `direnv`
```bash
direnv allow .
```
If you need to install `direnv`, [make sure you also modify the shell configuration](https://direnv.net/docs/hook.html).
1. Before we can create our configuration file, we’ll need to pick an L1 block to serve as the starting point for our Rollup. It’s best to use a finalized L1 block as our starting block. You can use the `cast` command provided by Foundry to grab all of the necessary information:
```bash
cast block finalized --rpc-url $ETH_RPC_URL | grep -E "(timestamp|hash|number)"
```
You’ll get back something that looks like the following:
```
hash 0x784d8e7f0e90969e375c7d12dac7a3df6879450d41b4cb04d4f8f209ff0c4cd9
number 8482289
timestamp 1676253324
```
1. Fill out the remainder of the pre-populated config file found at [`deploy-config/getting-started.json`](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/deploy-config/getting-started.json). Use the default values in the config file and make following modifications:
- Replace `"ADMIN"` with the address of the Admin account you generated earlier.
- Replace `"PROPOSER"` with the address of the Proposer account you generated earlier.
- Replace `"BATCHER"` with the address of the Batcher account you generated earlier.
- Replace `"SEQUENCER"` with the address of the Sequencer account you generated earlier.
- Replace `"BLOCKHASH"` with the blockhash you got from the `cast` command.
- Replace `TIMESTAMP` with the timestamp you got from the `cast` command. Note that although all the other fields are strings, this field is a number! Don’t include the quotation marks.
## Deploy the L1 contracts
Once you’ve configured your network, it’s time to deploy the L1 smart contracts necessary for the functionality of the chain.
1. Create a `getting-started` deployment directory.
```bash
mkdir deployments/getting-started
```
1. Once you’re ready, deploy the L1 smart contracts.
```bash
forge script scripts/Deploy.s.sol:Deploy --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
forge script scripts/Deploy.s.sol:Deploy --sig 'sync()' --private-key $PRIVATE_KEY --broadcast --rpc-url $ETH_RPC_URL
```
Contract deployment can take up to 15 minutes. Please wait for all smart contracts to be fully deployed before continuing to the next step.
## Generate the L2 config files
We’ve set up the L1 side of things, but now we need to set up the L2 side of things. We do this by generating three important files, a `genesis.json` file, a `rollup.json` configuration file, and a `jwt.txt` [JSON Web Token](https://jwt.io/introduction) that allows the `op-node` and `op-geth` to communicate securely.
1. Head over to the `op-node` package.
```bash
cd ~/optimism/op-node
```
1. Run the following command, and make sure to replace `<RPC>` with your L1 RPC URL:
```bash
go run cmd/main.go genesis l2 \
--deploy-config ../packages/contracts-bedrock/deploy-config/getting-started.json \
--deployment-dir ../packages/contracts-bedrock/deployments/getting-started/ \
--outfile.l2 genesis.json \
--outfile.rollup rollup.json \
--l1-rpc <RPC>
```
You should then see the `genesis.json` and `rollup.json` files inside the `op-node` package.
1. Next, generate the `jwt.txt` file with the following command:
```bash
openssl rand -hex 32 > jwt.txt
```
1. Finally, we’ll need to copy the `genesis.json` file and `jwt.txt` file into `op-geth` so we can use it to initialize and run `op-geth` in just a minute:
```bash
cp genesis.json ~/op-geth
cp jwt.txt ~/op-geth
```
## Initialize op-geth
We’re almost ready to run our chain! Now we just need to run a few commands to initialize `op-geth`. We’re going to be running a Sequencer node, so we’ll need to import the `Sequencer` private key that we generated earlier. This private key is what our Sequencer will use to sign new blocks.
1. Head over to the `op-geth` repository:
```bash
cd ~/op-geth
```
1. Create a data directory folder:
```bash
mkdir datadir
```
1. Next we need to initialize `op-geth` with the genesis file we generated and copied earlier:
```bash
build/bin/geth init --datadir=datadir genesis.json
```
Everything is now initialized and ready to go!
## Run the node software
There are four components that need to run for a rollup.
The first two, `op-geth` and `op-node`, have to run on every node.
The other two, `op-batcher` and `op-proposer`, run only in one place, the sequencer that accepts transactions.
Set these environment variables for the configuration
| Variable | Value |
| -------------- | -
| `SEQ_KEY` | Private key of the `Sequencer` account
| `BATCHER_KEY` | Private key of the `Batcher` accounts, which should have at least 1 ETH
| `PROPOSER_KEY` | Private key of the `Proposer` account
| `L1_RPC` | URL for the L1 (such as Goerli) you're using
| `RPC_KIND` | The type of L1 server to which you connect, which can optimize requests. Available options are `alchemy`, `quicknode`, `parity`, `nethermind`, `debug_geth`, `erigon`, `basic`, and `any`
| `L2OO_ADDR` | The address of the `L2OutputOracleProxy`, available at `~/optimism/packages/contracts-bedrock/deployments/getting-started/L2OutputOracleProxy.json`
### `op-geth`
Run `op-geth` with the following commands.
```bash
cd ~/op-geth
./build/bin/geth \
--datadir ./datadir \
--http \
--http.corsdomain="*" \
--http.vhosts="*" \
--http.addr=0.0.0.0 \
--http.api=web3,debug,eth,txpool,net,engine \
--ws \
--ws.addr=0.0.0.0 \
--ws.port=8546 \
--ws.origins="*" \
--ws.api=debug,eth,txpool,net,engine \
--syncmode=full \
--gcmode=archive \
--nodiscover \
--maxpeers=0 \
--networkid=42069 \
--authrpc.vhosts="*" \
--authrpc.addr=0.0.0.0 \
--authrpc.port=8551 \
--authrpc.jwtsecret=./jwt.txt \
--rollup.disabletxpoolgossip=true
```
And `op-geth` should be running! You should see some output, but you won’t see any blocks being created yet because `op-geth` is driven by the `op-node`. We’ll need to get that running next.
::: tip Why archive mode?
Archive mode takes more disk storage than full mode.
However, using it is important for two reasons:
- The `op-proposer` requires access to the full state.
If at some point `op-proposer` needs to look beyond 256 blocks in the past (8.5 minutes in the default configuration), for example because it was down for that long, we need archive mode.
- The [explorer](./explorer.md) requires archive mode.
:::
#### Reinitializing op-geth
There are several situations are indicate database corruption and require you to reset the `op-geth` component:
- When `op-node` errors out when first started and exits.
- When `op-node` emits this error:
```
stage 0 failed resetting: temp: failed to find the L2 Heads to start from: failed to fetch L2 block by hash 0x0000000000000000000000000000000000000000000000000000000000000000
```
This is the reinitialization procedure:
1. Stop the `op-geth` process.
1. Delete the geth data.
```bash
cd ~/op-geth
rm -rf datadir/geth
```
1. Rerun init.
```bash
build/bin/geth init --datadir=datadir genesis.json
```
1. Start `op-geth`
1. Start `op-node`
### `op-node`
Once we’ve got `op-geth` running we’ll need to run `op-node`. Like Ethereum, the OP Stack has a consensus client (the `op-node`) and an execution client (`op-geth`). The consensus client drives the execution client over the Engine API.
```bash
cd ~/optimism/op-node
./bin/op-node \
--l2=http://localhost:8551 \
--l2.jwt-secret=./jwt.txt \
--sequencer.enabled \
--sequencer.l1-confs=3 \
--verifier.l1-confs=3 \
--rollup.config=./rollup.json \
--rpc.addr=0.0.0.0 \
--rpc.port=8547 \
--p2p.disable \
--rpc.enable-admin \
--p2p.sequencer.key=$SEQ_KEY \
--l1=$L1_RPC \
--l1.rpckind=$RPC_KIND
```
Once you run this command, you should start seeing the `op-node` begin to process all of the L1 information after the starting block number that you picked earlier. Once the `op-node` has enough information, it’ll begin sending Engine API payloads to `op-geth`. At that point, you’ll start to see blocks being created inside of `op-geth`. We’re live!
::: tip Peer to peer synchronization
If you use a chain ID that is also used by others, for example the default (42069), your `op-node` will try to use peer to peer to speed up synchronization.
These attempts will fail, because they will be signed with the wrong key, but they will waste time and network resources.
To avoid this , we start with peer to peer synchronization disabled (`--p2p.disable`).
Once you have multiple nodes, it makes sense to use these command line parameters to synchronize between them without getting confused by other blockchains.
```
--p2p.static=<nodes> \
--p2p.listen.ip=0.0.0.0 \
--p2p.listen.tcp=9003 \
--p2p.listen.udp=9003 \
```
:::
### `op-batcher`
The `op-batcher` takes transactions from the Sequencer and publishes those transactions to L1. Once transactions are on L1, they’re officially part of the Rollup. Without the `op-batcher`, transactions sent to the Sequencer would never make it to L1 and wouldn’t become part of the canonical chain. The `op-batcher` is critical!
It is best to give the `Batcher` at least 1 Goerli ETH to ensure that it can continue operating without running out of ETH for gas.
```bash
cd ~/optimism/op-batcher
./bin/op-batcher \
--l2-eth-rpc=http://localhost:8545 \
--rollup-rpc=http://localhost:8547 \
--poll-interval=1s \
--sub-safety-margin=6 \
--num-confirmations=1 \
--safe-abort-nonce-too-low-count=3 \
--resubmission-timeout=30s \
--rpc.addr=0.0.0.0 \
--rpc.port=8548 \
--rpc.enable-admin \
--max-channel-duration=1 \
--l1-eth-rpc=$L1_RPC \
--private-key=$BATCHER_KEY
```
::: tip Controlling batcher costs
The `--max-channel-duration=n` setting tells the batcher to write all the data to L1 every `n` L1 blocks.
When it is low, transactions are written to L1 frequently, withdrawals are quick, and other nodes can synchronize from L1 fast.
When it is high, transactions are written to L1 less frequently, and the batcher spends less ETH.
:::
### `op-proposer`
Now start `op-proposer`, which proposes new state roots.
```bash
cd ~/optimism/op-proposer
./bin/op-proposer \
--poll-interval=12s \
--rpc.port=8560 \
--rollup-rpc=http://localhost:8547 \
--l2oo-address=$L2OO_ADDR \
--private-key=$PROPOSER_KEY \
--l1-eth-rpc=$L1_RPC
```
## Get some ETH on your Rollup
Once you’ve connected your wallet, you’ll probably notice that you don’t have any ETH on your Rollup. You’ll need some ETH to pay for gas on your Rollup. The easiest way to deposit Goerli ETH into your chain is to send funds directly to the `L1StandardBridge` contract. You can find the address of the `L1StandardBridge` contract for your chain by looking inside the `deployments` folder in the `contracts-bedrock` package.
1. First, head over to the `contracts-bedrock` package:
```bash
cd ~/optimism/packages/contracts-bedrock
```
1. Grab the address of the proxy to the L1 standard bridge contract:
```bash
cat deployments/getting-started/L1StandardBridgeProxy.json | jq -r .address
```
1. Grab the L1 bridge proxy contract address and, using the wallet that you want to have ETH on your Rollup, send that address a small amount of ETH on Goerli (0.1 or less is fine). It may take up to 5 minutes for that ETH to appear in your wallet on L2.
## Use your Rollup
Congratulations, you made it! You now have a complete OP Stack based EVM Rollup.
To see your rollup in action, you can use the [Optimism Mainnet Getting Started tutorial](https://github.com/ethereum-optimism/optimism-tutorial/blob/main/getting-started). Follow these steps:
1. Clone the tutorials repository.
```bash
cd ~
git clone https://github.com/ethereum-optimism/optimism-tutorial.git
```
1. Change to the Foundry directory of the Getting Started tutorial.
```bash
cd optimism-tutorial/getting-started/foundry
```
1. Put your mnemonic (for the address where you have ETH, the one that sent ETH to `OptimismPortalProxy` on Goerli) in a file `mnem.delme`.
1. Provide the URL to your blockchain:
```bash
export ETH_RPC_URL=http://localhost:8545
```
1. Compile and deploy the `Greeter` contract:
```bash
forge create --mnemonic-path ./mnem.delme Greeter --constructor-args "hello" \
| tee deployment
```
1. Set the greeter to the deployed to address:
```bash
export GREETER=`cat deployment | awk '/Deployed to:/ {print $3}'`
echo $GREETER
```
1. See and modify the greeting
```bash
cast call $GREETER "greet()" | cast --to-ascii
cast send --mnemonic-path ./mnem.delme $GREETER "setGreeting(string)" "New greeting"
cast call $GREETER "greet()" | cast --to-ascii
```
To use any other development stack, see the getting started tutorial, just replace the Greeter address with the address of your rollup, and the Optimism Goerli URL with `http://localhost:8545`.
### Errors
#### Corrupt data directory
If `op-geth` aborts (for example, because the computer it is running on crashes), you might get these errors on `op-node`:
```
WARN [02-16|21:22:02.868] Derivation process temporary error attempts=14 err="stage 0 failed resetting: temp: failed to find the L2 Heads to start from: failed to fetch L2 block by hash 0x0000000000000000000000000000000000000000000000000000000000000000: failed to determine block-hash of hash 0x0000000000000000000000000000000000000000000000000000000000000000, could not get payload: not found"
```
This means that the data directory is corrupt and you need to reinitialize it:
```bash
cd ~/op-geth
rm -rf datadir
mkdir datadir
echo "pwd" > datadir/password
echo "<SEQUENCER KEY HERE>" > datadir/block-signer-key
./build/bin/geth account import --datadir=./datadir --password=./datadir/password ./datadir/block-signer-key
./build/bin/geth init --datadir=./datadir ./genesis.json
```
#### Batcher out of ETH
If `op-batcher` runs out of ETH, it cannot submit write new transaction batches to L1.
You will get error messages similar to this one:
```
INFO [03-21|14:22:32.754] publishing transaction service=batcher txHash=2ace6d..7eb248 nonce=2516 gasTipCap=2,340,741 gasFeeCap=172,028,434,515
ERROR[03-21|14:22:32.844] unable to publish transaction service=batcher txHash=2ace6d..7eb248 nonce=2516 gasTipCap=2,340,741 gasFeeCap=172,028,434,515 err="insufficient funds for gas * price + value"
```
Just send more ETH and to the batcher, and the problem will be resolved.
## What’s next?
You can use this rollup the same way you’d use any other test blockchain. Once the superchain is available, this blockchain should be able to join the test version. Alternatively, you could [modify the blockchain in various ways](./hacks.md). **Please note that OP Stack Hacks are unofficial and are not explicitly supported by the OP Stack.** You will not be able to receive significant developer support for any modifications you make to the OP Stack.
---
title: Introduction to OP Stack Hacks
lang: en-US
---
Welcome to OP Stack Hacks, the **highly experimental** region of the OP Stack docs. OP Stack Hacks are an unofficial guide for messing around with the OP Stack. Here you’ll find information about ways that the OP Stack can be modified in interesting ways.
OP Stack Hacks create blockchains that aren’t exactly OP Stack, and may be insecure. Hacked OP Stack chains can break key invariants that are required to interoperate with [the Optimism Superchain](../understand/explainer.md). **Developers of chains that wish to interoperate with [the Optimism Superchain](../understand/explainer.md) should *not* include any hacks**. When in doubt, stick with the official components within [the current release of the OP Stack](../releases/README.md#current-release).
::: warning 🚧 OP Stack Hacks are explicitly things that you can do with the OP Stack that are *not* currently intended for production use
OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.
:::
---
title: Rollup Operations
lang: en-US
---
## Stopping your Rollup
An orderly shutdown is done in the reverse order to the order in which components were started:
1. To stop the batcher, use this command:
```sh
curl -d '{"id":0,"jsonrpc":"2.0","method":"admin_stopBatcher","params":[]}' \
-H "Content-Type: application/json" http://localhost:8548 | jq
```
This way the batcher knows to save any data it has cached to L1.
Wait until you see `Batch Submitter stopped` in batcher's output before you stop the process.
1. Stop `op-node`.
This component is stateless, so you can just stop the process.
1. Stop `op-geth`.
Make sure you use **CTRL-C** to avoid database corruption.
## Starting your Rollup
To restart the blockchain, use the same order of components you did when you initialized it.
1. `op-geth`
1. `op-node`
1. `op-batcher`
If `op-batcher` is still running and you just stopped it using RPC, you can start it with this command:
```sh
curl -d '{"id":0,"jsonrpc":"2.0","method":"admin_startBatcher","params":[]}' \
-H "Content-Type: application/json" http://localhost:8548 | jq
```
::: tip Synchronization takes time
`op-batcher` might have warning messages similar to:
```
WARN [03-21|14:13:55.248] Error calculating L2 block range err="failed to get sync status: Post \"http://localhost:8547\": context deadline exceeded"
WARN [03-21|14:13:57.328] Error calculating L2 block range err="failed to get sync status: Post \"http://localhost:8547\": context deadline exceeded"
```
This means that `op-node` is not yet synchronized up to the present time.
Just wait until it is.
:::
## Adding nodes
To add nodes to the rollup, you need to initialize `op-node` and `op-geth`, similar to what you did for the first node.
You should *not* add an `op-bathcer`, there should be only one.
1. Configure the OS and prerequisites as you did for the first node.
1. Build the Optimism monorepo and `op-geth` as you did for the first node.
1. Copy from the first node these files:
```bash
~/op-geth/genesis.json
~/optimism/op-node/rollup.json
```
1. Create a new `jwt.txt` file as a shared secret:
```bash
cd ~/op-geth
openssl rand -hex 32 > jwt.txt
cp jwt.txt ~/optimism/op-node
```
1. Initialize the new op-geth:
```bash
cd ~/op-geth
./build/bin/geth init --datadir=./datadir ./genesis.json
```
1. To enable L2 nodes to synchronize directly, rather than wait until the transactions are written to L1, turn on [peer to peer synchronization](http://localhost:8081/docs/build/getting-started/#run-op-node).
If you already have peer to peer synchronization, add the new node to the `--p2p.static` list so it can synchronize.
1. Start `op-geth` (using the same command line you used on the initial node)
1. Start `op-node` (using the same command line you used on the initial node)
---
title: Using the OP Stack Client SDK
lang: en-US
---
## Natively supported chains
[The OP Stack Client SDK](https://sdk.optimism.io/) natively supports multiple OP Chains: OP, Base, etc.
To see whether a specific OP Chain is supported directly, [see the documentation](https://sdk.optimism.io/enums/l2chainid).
## Not natively supported chains
If you are using a chain that is *not* natively supported, for example an OP Stack chain [you just created](./getting-started.md), you can continue to use [the OP Stack Client SDK](https://sdk.optimism.io/).
You just need to provide some contract addresses to the `CrossDomainMessenger` because they aren't preconfigured.
### Getting contract addresses
#### L1 contract addresses
If you followed the directions in [Getting Started](./getting-started.md), the contract addresses are in `.../optimism/packages/contracts-bedrock/deployments/getting-started`, which you created when you deployed the L1 contracts.
| Contract name when creating `CrossDomainMessenger` | File with address |
| - | - |
| `AddressManager` | `Lib_AddressManager.json`
| `L1CrossDomainMessenger` | `Proxy__OVM_L1CrossDomainMessenger.json`
| `L1StandardBridge` | `Proxy__OVM_L1StandardBridge.json`
| `OptimismPortal` | `OptimismPortalProxy.json`
| `L2OutputOracle` | `L2OutputOracleProxy.json`
#### Unneeded contract addresses
Some contracts are required by the SDK as a sanity check, but are not actually used.
For these contracts you can just specify the zero address:
- `StateCommitmentChain`
- `CanonicalTransactionChain`
- `BondManager`
In JavaScript you can create the zero address using the expression `"0x".padEnd(42, "0")`.
### The CrossChainMessenger object
These directions assume you are inside the [Hardhat console](https://hardhat.org/hardhat-runner/docs/guides/hardhat-console).
They further assume that your project already includes the Optimism SDK [`@eth-optimism/sdk`](https://www.npmjs.com/package/@eth-optimism/sdk).
1. Import the SDK
```js
optimismSDK = require("@eth-optimism/sdk")
```
1. Set the configuration parameters.
| Variable name | Value |
| - | - |
| `l1Url` | URL to an RPC provider for L1, for example `https://eth-goerli.g.alchemy.com/v2/<api key>`
| `l2Url` | URL to your OP Stack. If running on the same computer, it is `http://localhost:8545`
| `privKey` | The private key for an account that has some ETH on the L1
1. Create the [providers](https://docs.ethers.org/v5/api/providers/) and [signers](https://docs.ethers.org/v5/api/signer/).
```js
l1Provider = new ethers.providers.JsonRpcProvider(l1Url)
l2Provider = new ethers.providers.JsonRpcProvider(l2Url)
l1Signer = new ethers.Wallet(privKey).connect(l1Provider)
l2Signer = new ethers.Wallet(privKey).connect(l2Provider)
```
1. Create the L1 contracts structure.
```js
zeroAddr = "0x".padEnd(42, "0")
l1Contracts = {
StateCommitmentChain: zeroAddr,
CanonicalTransactionChain: zeroAddr,
BondManager: zeroAddr,
// These contracts have the addresses you found out earlier.
AddressManager: "0x....", // Lib_AddressManager.json
L1CrossDomainMessenger: "0x....", // Proxy__OVM_L1CrossDomainMessenger.json
L1StandardBridge: "0x....", // Proxy__OVM_L1StandardBridge.json
OptimismPortal: "0x....", // OptimismPortalProxy.json
L2OutputOracle: "0x....", // L2OutputOracleProxy.json
}
```
1. Create the data structure for the standard bridge.
```js
bridges = {
Standard: {
l1Bridge: l1Contracts.L1StandardBridge,
l2Bridge: "0x4200000000000000000000000000000000000010",
Adapter: optimismSDK.StandardBridgeAdapter
},
ETH: {
l1Bridge: l1Contracts.L1StandardBridge,
l2Bridge: "0x4200000000000000000000000000000000000010",
Adapter: optimismSDK.ETHBridgeAdapter
}
}
```
1. Create the [`CrossChainMessenger`](https://sdk.optimism.io/classes/crosschainmessenger) object.
```js
crossChainMessenger = new optimismSDK.CrossChainMessenger({
bedrock: true,
contracts: {
l1: l1Contracts
},
bridges: bridges,
l1ChainId: await l1Signer.getChainId(),
l2ChainId: await l2Signer.getChainId(),
l1SignerOrProvider: l1Signer,
l2SignerOrProvider: l2Signer,
})
```
### Verify SDK functionality
To verify the SDK's functionality, transfer some ETH from L1 to L2.
1. Get the current balances.
```js
balances0 = [
await l1Provider.getBalance(l1Signer.address),
await l2Provider.getBalance(l1Signer.address)
]
```
1. Transfer 1 gwei.
```js
tx = await crossChainMessenger.depositETH(1e9)
rcpt = await tx.wait()
```
1. Get the balances after the transfer.
```js
balances1 = [
await l1Provider.getBalance(l1Signer.address),
await l2Provider.getBalance(l1Signer.address)
]
```
1. See that the L1 balance changed (probably by a lot more than 1 gwei because of the cost of the transaction).
```js
(balances0[0]-balances1[0])/1e9
```
1. See that the L2 balance changed (it might take a few minutes).
```js
((await l2Provider.getBalance(l1Signer.address))-balances0[1])/1e9
```
---
title: Settlement Hacks
lang: en-US
---
::: warning 🚧 OP Stack Hacks are explicitly things that you can do with the OP Stack that are *not* currently intended for production use
OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.
:::
# Overview
The Settlement Layer includes modules that are used by third-party chains to establish a *view* of the state of your OP Stack chain. This view can then be used by applications on those chains to make decisions based on the state of your OP Stack chain. Third-party chains can be any other blockchain, including other OP Stack chains. One common Settlement Layer mechanism is a withdrawal system that allows users to send assets from your OP Stack chain to the third-party chain. Modifications to this layer typically involve introducing new modules or tweaking the security model of existing modules.
## Default
The default Settlement Layer module is currently the Attestation Proof Optimistic Settlement module. This module allows a third-party chain to become aware of the state of an OP Stack chain through an Optimistic protocol where challenges can be executed alongside a threshold of attestations from a pre-defined set of addresses over a state that differs from the proposed state. With a Cannon fault proof shipped to production, this default module can be replaced with a module that allows anyone to challenge proposals by playing the Cannon dispute game.
## Security
Modifications to the Settlement Layer can strongly impact the security of common mechanisms like user withdrawals. A decreased withdrawal delay can, for instance, open the door to gas spam attacks that make challenges exceedingly expensive. It is generally not recommended to modify the Settlement Layer unless you know what you’re doing.
## Modding
### Tweaked parameters
One simple modification to the Settlement Layer is to tweak the parameters of the default Optimistic asset withdrawal mechanism. For example, the withdrawal period can be reduced if a smaller withdrawal period would be sufficient to secure your system.
### Custom proofs
Settlement Layer modules use a proof system to verify the correctness of the state of your OP Stack chain as proposed on the third-party chain. In general, these proofs are either Optimistic proofs that require a withdrawal delay or Validity proofs that use a mathematical proof system to assert the validity of the proposal. The current Attestation Proof Optimistic Settlement module could be replaced with a fault proof system.
### Multiple modules
There is no requirement that a system only have one Settlement Layer module. It is possible to use one or more Settlement Layer modules on one or more third-party chains. A system that aims to bridge assets between two chains will likely need to use one Data Availability Layer module and one Settlement Layer module per chain.
\ No newline at end of file
---
title: Adding Attributes to the Derivation Function
lang: en-US
---
::: warning 🚧 OP Stack Hacks are explicitly things that you can do with the OP Stack that are *not* currently intended for production use
OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.
:::
## Overview
In this guide, we’ll modify the Bedrock Rollup. Although there are many ways to modify the OP Stack, we’re going to spend this tutorial modifying the Derivation function. Specifically, we’re going to update the Derivation function to track the amount of ETH being burned on L1! Who’s gonna tell [ultrasound.money](http://ultrasound.money) that they should replace their backend with an OP Stack chain?
## Getting the idea
Let’s quickly recap what we’re about to do. The `op-node` is responsible for generating the Engine API payloads that trigger `op-geth` to produce blocks and transactions. The `op-node` already generates a “system transaction” for every L1 block that relays information about the current L1 state to the L2 chain. We’re going to modify the `op-node` to add a new system transaction that reports the total burn amount (the base fee multiplied by the gas used) in each block.
Although it might sound like a lot, the whole process only involves deploying a single smart contract, adding one new file to `op-node`, and modifying one existing file inside `op-node`. It’ll be painless. Let’s go!
## Deploy the burn contract
We’re going to use a smart contract on our Rollup to store the reports that the `op-node` makes about the L1 burn. Here’s the code for our smart contract:
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title L1Burn
* @notice L1Burn keeps track of the total amount of ETH burned on L1.
*/
contract L1Burn {
/**
* @notice Total amount of ETH burned on L1.
*/
uint256 public total;
/**
* @notice Mapping of blocks numbers to total burn.
*/
mapping (uint64 => uint256) public reports;
/**
* @notice Allows the system address to submit a report.
*
* @param _blocknum L1 block number the report corresponds to.
* @param _burn Amount of ETH burned in the block.
*/
function report(uint64 _blocknum, uint64 _burn) external {
require(
msg.sender == 0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001,
"L1Burn: reports can only be made from system address"
);
total += _burn;
reports[_blocknum] = total;
}
/**
* @notice Tallies up the total burn since a given block number.
*
* @param _blocknum L1 block number to tally from.
*
* @return Total amount of ETH burned since the given block number;
*/
function tally(uint64 _blocknum) external view returns (uint256) {
return total - reports[_blocknum];
}
}
```
Deploy this smart contract to your L2 (using any tool you find convenient). Make a note of the address that the contract is deployed to because you’ll need it in a minute. Simple!
## Add the burn transaction
Now we need to add logic to the `op-node` to automatically submit a burn report whenever an L1 block is produced. Since this transaction is very similar to the system transaction that reports other L1 block info (found in [l1_block_info.go](https://github.com/ethereum-optimism/optimism/blob/c9cd1215b76111888e25ee27a49a0bc0c4eeb0f8/op-node/rollup/derive/l1_block_info.go)), we’ll use that transaction as a jumping-off point.
1. Navigate to the `op-node` package:
```bash
cd ~/optimism/op-node
```
1. Inside of the folder `rollup/derive`, create a new file called `l1_burn_info.go`:
```bash
touch rollup/derive/l1_burn_info.go
```
1. Paste the following into `l1_burn_info.go`, and make sure to replace `YOUR_BURN_CONTRACT_HERE` with the address of the `L1Burn` contract you just deployed.
```go
package derive
import (
"bytes"
"encoding/binary"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum-optimism/optimism/op-node/eth"
)
const (
L1BurnFuncSignature = "report(uint64,uint64)"
L1BurnArguments = 2
L1BurnLen = 4 + 32*L1BurnArguments
)
var (
L1BurnFuncBytes4 = crypto.Keccak256([]byte(L1BurnFuncSignature))[:4]
L1BurnAddress = common.HexToAddress("YOUR_BURN_CONTRACT_HERE")
)
type L1BurnInfo struct {
Number uint64
Burn uint64
}
func (info *L1BurnInfo) MarshalBinary() ([]byte, error) {
data := make([]byte, L1BurnLen)
offset := 0
copy(data[offset:4], L1BurnFuncBytes4)
offset += 4
binary.BigEndian.PutUint64(data[offset+24:offset+32], info.Number)
offset += 32
binary.BigEndian.PutUint64(data[offset+24:offset+32], info.Burn)
return data, nil
}
func (info *L1BurnInfo) UnmarshalBinary(data []byte) error {
if len(data) != L1InfoLen {
return fmt.Errorf("data is unexpected length: %d", len(data))
}
var padding [24]byte
offset := 4
info.Number = binary.BigEndian.Uint64(data[offset+24 : offset+32])
if !bytes.Equal(data[offset:offset+24], padding[:]) {
return fmt.Errorf("l1 burn tx number exceeds uint64 bounds: %x", data[offset:offset+32])
}
offset += 32
info.Burn = binary.BigEndian.Uint64(data[offset+24 : offset+32])
if !bytes.Equal(data[offset:offset+24], padding[:]) {
return fmt.Errorf("l1 burn tx burn exceeds uint64 bounds: %x", data[offset:offset+32])
}
return nil
}
func L1BurnDepositTxData(data []byte) (L1BurnInfo, error) {
var info L1BurnInfo
err := info.UnmarshalBinary(data)
return info, err
}
func L1BurnDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfig) (*types.DepositTx, error) {
infoDat := L1BurnInfo{
Number: block.NumberU64(),
Burn: block.BaseFee().Uint64() * block.GasUsed(),
}
data, err := infoDat.MarshalBinary()
if err != nil {
return nil, err
}
source := L1InfoDepositSource{
L1BlockHash: block.Hash(),
SeqNumber: seqNumber,
}
return &types.DepositTx{
SourceHash: source.SourceHash(),
From: L1InfoDepositerAddress,
To: &L1BurnAddress,
Mint: nil,
Value: big.NewInt(0),
Gas: 150_000_000,
IsSystemTransaction: true,
Data: data,
}, nil
}
func L1BurnDepositBytes(seqNumber uint64, l1Info eth.BlockInfo, sysCfg eth.SystemConfig) ([]byte, error) {
dep, err := L1BurnDeposit(seqNumber, l1Info, sysCfg)
if err != nil {
return nil, fmt.Errorf("failed to create L1 burn tx: %w", err)
}
l1Tx := types.NewTx(dep)
opaqueL1Tx, err := l1Tx.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("failed to encode L1 burn tx: %w", err)
}
return opaqueL1Tx, nil
}
```
Feel free to take a look at this file if you’re interested. It’s relatively simple, mainly just defining a new transaction type and describing how the transaction should be encoded.
## Insert the burn transactions
Finally, we’ll need to update `~/optimism/op-node/rollup/derive/attributes.go` to insert the new burn transaction into every block. You’ll need to make the following changes:
1. Find these lines:
```go
l1InfoTx, err := L1InfoDepositBytes(seqNumber, l1Info, sysConfig)
if err != nil {
return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err))
}
```
1. After those lines, add this code fragment:
```go
l1BurnTx, err := L1BurnDepositBytes(seqNumber, l1Info, sysConfig)
if err != nil {
return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err))
}
```
1. Immediately following, change these lines:
```go
txs := make([]hexutil.Bytes, 0, 1+len(depositTxs))
txs = append(txs, l1InfoTx)
```
to
```go
txs := make([]hexutil.Bytes, 0, 2+len(depositTxs))
txs = append(txs, l1InfoTx)
txs = append(txs, l1BurnTx)
```
All we’re doing here is creating a new burn transaction after every `l1InfoTx` and inserting it into every block.
## Rebuild your op-node
Before we can see this change take effect, you’ll need to rebuild your `op-node`:
```bash
cd ~/optimism/op-node
make op-node
```
Now start your `op-node` if it isn’t running or restart your `op-node` if it’s already running. You should see the change immediately — new blocks will contain two system transactions instead of just one!
## Checking the result
Query the `total` function of your contract, you should also start to see the total slowly increasing. Play around with the `tally` function to grab the amount of gas burned since a given L2 block. You could use this to implement a version of [ultrasound.money](http://ultrasound.money) that keeps track of things with an OP Stack as a backend. We did it reddit!
One way to get the total is to run these commands:
```bash
export ETH_RPC_URL=http://localhost:8545
cast call <YOUR_BURN_CONTRACT_HERE> "total()" | cast --from-wei
```
## Conclusion
With just a few tiny changes to the `op-node`, you were just able to implement a change to the OP Stack that allows you to keep track of the L1 ETH burn on L2. With a live Cannon fault proof system, you should not only be able to track the L1 burn on L2, you should be able to *prove* the burn to contracts back on L1. You could build a trustless prediction market on the amount of ETH burned. That’s crazy!
The OP Stack is an extremely powerful platform that allows you to perform a large amount of computation trustlessly. It’s a superpower for smart contracts. Tracking the L1 burn is just one of the many, many wild things you can do with the OP Stack. If you’re looking for inspiration or you want to see what others are building on the OP Stack, check out our OP Stack Hacks page. Maybe you’ll find a project you want to work on, or maybe you’ll get the inspiration you need to build the next killer smart contract.
\ No newline at end of file
---
title: Adding a Precompile
lang: en-US
---
::: warning 🚧 OP Stack Hacks are explicitly things that you can do with the OP Stack that are *not* currently intended for production use
OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.
:::
One possible use of OP Stack is to run an EVM with a new precompile for operations to speed up calculations that are not currently supported. In this case we’ll make a simple precompile that returns a constant value if it’s called with four or less bytes, or an error if it is called with more than that.
To create a new precompile, the file to modify is [`op-geth/core/vm/contracts.go`](https://github.com/ethereum-optimism/op-geth/blob/optimism-history/core/vm/contracts.go).
1. Add to `PrecompiledContractsBerlin` on line 82 (or a later fork, if the list of precompiles changes again) a structure named after your new precompile, with an address that is unlikely to ever clash with a standard precompile (0x100, for example):
```go
common.BytesToAddress([]byte{1,0}): &retConstant{},
```
1. Add the lines for the precompile.
```go
type retConstant struct{}
func (c *retConstant) RequiredGas(input []byte) uint64 {
return uint64(1024)
}
var (
errConstInvalidInputLength = errors.New("invalid input length")
)
func (c *retConstant) Run(input []byte) ([]byte, error) {
// Only allow input up to four bytes (function signature)
if len(input) > 4 {
return nil, errConstInvalidInputLength
}
output := make([]byte, 6)
for i := 0; i < 6; i++ {
output[i] = byte(64+i)
}
return output, nil
}
```
1. Stop `op-geth` and recompile:
```bash
cd ~/op-geth
make geth
```
1. Restart `op-geth`.
1. Run these command to see the result of calling the precompile successfully, and the result of an error:
```bash
cast call 0x0000000000000000000000000000000000000100 "whatever()"
cast call 0x0000000000000000000000000000000000000100 "whatever(string)" "fail"
```
## How does it work?
This is the precompile interface definition:
```go
type PrecompiledContract interface {
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
}
```
It means that for every precompile we need two functions:
- `RequiredGas` which returns the gas cost for the call. This function takes an array of bytes as input, and returns a single value, the gas cost.
- `Run` which runs the actual precompile. This function also takes an array of bytes, but it returns two values: the call’s output (a byte array) and an error.
For every fork that changes the precompiles we have a [`map`](https://www.w3schools.com/go/go_maps.php)from addresses to the `PrecompiledContract` definitions:
```go
// PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum
// contracts used in the Berlin release.
var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
common.BytesToAddress([]byte{1}): &ecrecover{},
.
.
.
common.BytesToAddress([]byte{9}): &blake2F{},
common.BytesToAddress([]byte{1,0}): &retConstant{},
}
```
The key of the map is an address. We create those from bytes using `common.BytesToAddress([]byte{<bytes to convert to address go here>})`. In this case we have two bytes, `0x01` and `0x00`. Together we get the address `0x0…0100`.
The syntax for a precompiled contract interface is `&<variable name>{}`.
The next step is to define the precompiled contract itself.
```go
type retConstant struct{}
```
First we create a structure for the precompile.
```go
func (c *retConstant) RequiredGas(input []byte) uint64 {
return uint64(1024)
}
```
Then we define a function as part of that structure. Here we just require a constant amount of gas, but of course the calculation can be a lot more sophisticated.
```go
var (
errConstInvalidInputLength = errors.New("invalid input length")
)
```
Next we define a variable for the error.
```go
func (c *retConstant) Run(input []byte) ([]byte, error) {
```
This is the function that actually executes the precompile.
```go
// Only allow input up to four bytes (function signature)
if len(input) > 4 {
return nil, errConstInvalidInputLength
}
```
Return an error if warranted. The reason this precompile allows up to four bytes of input is that any standard call (for example, using `cast`) is going to have at least four bytes for the function signature.
`return a, b` is the way we return two values from a function in Go. The normal output is `nil`, nothing, because we return an error.
```go
output := make([]byte, 6)
for i := 0; i < 6; i++ {
output[i] = byte(64+i)
}
return output, nil
}
```
Finally, we create the output buffer, fill it, and then return it.
## Conclusion
An OP Stack chain with additional precompiles can be useful, for example, to further reduce the computational effort required for cryptographic operations by moving them from interpreted EVM code to compiled Go code.
\ No newline at end of file
---
title: Modifying Predeployed Contracts
lang: en-US
---
::: warning 🚧 OP Stack Hacks are explicitly things that you can do with the OP Stack that are *not* currently intended for production use
OP Stack Hacks are not for the faint of heart. You will not be able to receive significant developer support for OP Stack Hacks — be prepared to get your hands dirty and to work without support.
:::
OP Stack blockchains have a number of [predeployed contracts](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/src/constants.ts) that provide important functionality.
Most of those contracts are proxies that can be upgraded using the `proxyAdminOwner` which was configured when the network was initially deployed.
The predeploys are controlled from a predeploy called [`ProxyAdmin`](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/contracts/universal/ProxyAdmin.sol), whose address is `0x4200000000000000000000000000000000000018`.
The function to call is [`upgrade(address,address)`](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/contracts/universal/ProxyAdmin.sol#L205-L229).
The first parameter is the proxy to upgrade, and the second is the address of a new implementation.
For example, the legacy `L1BlockNumber` contract is at `0x420...013`.
To disable this function, we'll set the implementation to `0x00...00`.
We do this using the [Foundry](https://book.getfoundry.sh/) command `cast`.
1. We'll need several constants.
- Set these addresses as variables in your terminal.
```sh
L1BLOCKNUM=0x4200000000000000000000000000000000000013
PROXY_ADMIN=0x4200000000000000000000000000000000000018
ZERO_ADDR=0x0000000000000000000000000000000000000000
```
- Set `PRIVKEY` to the private key of your ADMIN account.
- Set `ETH_RPC_URL`. If you're on the computer that runs the blockchain, use this command.
```sh
export ETH_RPC_URL=http://localhost:8545
```
1. Verify `L1BlockNumber` works correctly.
See that when you call the contract you get a block number, and twelve seconds later you get the next one (block time on L1 is twelve seconds).
```sh
cast call $L1BLOCKNUM 'number()' | cast --to-dec
sleep 12 && cast call $L1BLOCKNUM 'number()' | cast --to-dec
```
1. Get the current implementation for the contract.
```sh
L1BLOCKNUM_IMPLEMENTATION=`cast call $L1BLOCKNUM "implementation()" | sed 's/000000000000000000000000//'`
echo $L1BLOCKNUM_IMPLEMENTATION
```
1. Change the implementation to the zero address
```sh
cast send --private-key $PRIVKEY $PROXY_ADMIN "upgrade(address,address)" $L1BLOCKNUM $ZERO_ADDR
```
1. See that the implementation is address zero, and that calling it fails.
```sh
cast call $L1BLOCKNUM 'implementation()'
cast call $L1BLOCKNUM 'number()'
```
1. Fix the predeploy by returning it to the previous implementation, and verify it works.
```sh
cast send --private-key $PRIVKEY $PROXY_ADMIN "upgrade(address,address)" $L1BLOCKNUM $L1BLOCKNUM_IMPLEMENTATION
cast call $L1BLOCKNUM 'number()' | cast --to-dec
```
\ No newline at end of file
---
title: Contribute to the OP Stack
lang: en-US
---
The OP Stack is a collaborative, decentralized development stack that only gets more powerful as more people contribute. Code for the OP Stack should follow the stack’s [design principles](./understand/design-principles.md), which means it should be entirely open source and accessible for people to hack on, contribute to, and extend. The Optimism Collective wins when it works together. ♥️✨
Whether you’re a budding protocol developer, dapp developer, bounty hunter, documentation editor, content creator, or anything in between, the OP Stack always has something for you to contribute to. Every contribution makes a difference — no contribution is too small.
If you’re looking to find a way to contribute, check out one of the following contributor pathways below. Come make your first contribution today!
## Component contributions
The OP Stack is a decentralized development stack. Anyone can contribute components that can be considered part of the OP Stack as long as as those components fit [the stack’s design principles and goals](./understand/design-principles.md). To start contributing components to the stack, check out some of [these useful ideas](https://github.com/ethereum-optimism/optimism-project-ideas) and get to building! And don’t forget that projects can also receive funding from the Collective via RetroPGF.
If you’d like to contribute to existing OP Stack code, rather than creating new components, check out [the current release of the OP Stack](./releases/README.md#current-release). Any contributions to existing OP Stack components are highly appreciated. If you’re looking for a good way to make your first contribution, check out the [Good First Issues](https://github.com/ethereum-optimism/optimism/contribute) on the Optimism Monorepo.
## Bounty hunting
The OP Stack needs YOU (yes you!) to help review the codebase for bugs and vulnerabilities. If you’re interested in bounty hunting, check out our Security Policy, Vulnerability Reporting, and Bug Bounties page.
## Documentation help
Spot a typo in these docs? See something that could be a little clearer? Head over to the Optimism Monorepo where the OP Stack docs are hosted and make a pull request. No contribution is too small!
## Community contributions
If you’re looking for other ways to get involved, here are a few options:
- Grab an idea from the [project ideas list](https://github.com/ethereum-optimism/optimism-project-ideas) to and building
- Suggest a new idea for the [project ideas list](https://github.com/ethereum-optimism/optimism-project-ideas)
- Improve the [Optimism Community Hub](https://community.optimism.io/) [documentation](https://github.com/ethereum-optimism/community-hub) or [tutorials](https://github.com/ethereum-optimism/optimism-tutorial)
- Become an Optimism Ambassador, Support Nerd, and more in the [Optimism Discord](https://discord.gg/optimism)
---
title: Release History
lang: en-US
---
The OP Stack codebase is a decentralized development stack maintained by the Optimism Collective and built to power Optimism.
The OP Stack is constantly evolving as new layers and modules are developed. The OP Stack codebase is also not a product (in the traditional sense) but rather a collection of software components that power the Optimism ecosystem.
**A “Release” of the OP Stack codebase is a particular set of software components that are production-ready and which fit the stack’s design principles and goals.**
Only the software components included within the Current Release of the OP Stack codebase are considered in the scope of the OP Stack. Any usage of the OP Stack outside of the official, intended capabilities of the Current Release are considered [OP Stack Hacks](../build/hacks.md) — unofficial modifications that are useful for experimentation but could have unforeseen results, such as security vulnerabilities, and are likely cause your chain to no longer be interoperable with [the Optimism Superchain](https://app.optimism.io/superchain/). **Developer support for OP Stack Hacks is limited — when in doubt, stick to the capabilities of the Current Release!**
## Current Release
[OP Stack codebase V1: Bedrock](./bedrock/)
## Past Releases
N/A
\ No newline at end of file
---
title: OP Stack codebase V1 - Bedrock
lang: en-US
---
## Overview
The first release of the OP Stack codebase is called **Bedrock**.
The Bedrock release primarily consists of the core software required to run L2 blockchains and was originally designed to power an upgrade to the Optimism Mainnet network.
## Resources
### Rollup Protocol
Learn about the basics of the Rollup protocol used by Bedrock on the [Rollup Protocol](https://community.optimism.io/docs/protocol/2-rollup-protocol/) page.
### Bedrock Explainer
Learn all about the Bedrock release of the OP Stack by reading the [Bedrock Explainer](./explainer.md).
### Specifications
Dive deep into the specifications for the Bedrock release in the [specs folder of the Optimism Monorepo](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/README.md).
## Components
- [`op-node`](https://github.com/ethereum-optimism/optimism/tree/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/op-node)
- [`op-geth`](https://github.com/ethereum-optimism/op-geth)
- [`op-batcher`](https://github.com/ethereum-optimism/optimism/tree/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/op-batcher)
- [`op-proposer`](https://github.com/ethereum-optimism/optimism/tree/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/op-proposer)
- [`contracts-bedrock`](https://github.com/ethereum-optimism/optimism/tree/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/packages/contracts-bedrock)
- [`fault-detector`](https://github.com/ethereum-optimism/optimism/tree/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/packages/fault-detector)
- [`sdk`](https://github.com/ethereum-optimism/optimism/tree/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/packages/sdk)
- [`chain-mon`](https://github.com/ethereum-optimism/optimism/tree/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/packages/chain-mon)
---
title: Differences between Bedrock and L1 Ethereum
lang: en-US
---
It's important to note that there are various minor discrepancies between the behavior of Optimism and Ethereum.
You should be aware of these descrepancies when building apps on top of Optimism or the OP Stack codebase.
## Opcode Differences
| Opcode | Solidity equivalent | Behavior |
| - | - | - |
| `COINBASE` | `block.coinbase` | Undefined |
| `DIFFICULTY` | `block.difficulty` | Random value. As this value is set by the sequencer, it is not as reliably random as the L1 equivalent. |
| `NUMBER` | `block.number` | L2 block number
| `TIMESTAMP` | `block.timestamp` | Timestamp of the L2 block
| `ORIGIN` | `tx.origin` | If the transaction is an L1 ⇒ L2 transaction, then `tx.origin` is set to the [aliased address](#address-aliasing) of the address that triggered the L1 ⇒ L2 transaction. Otherwise, this opcode behaves normally. |
| `CALLER` | `msg.sender` | If the transaction is an L1 ⇒ L2 transaction, and this is the initial call (rather than an internal transaction from one contract to another), the same [address aliasing](#address-aliasing) behavior applies.
::: tip `tx.origin == msg.sender`
On L1 Ethereum `tx.origin` is equal to `msg.sender` only when the smart contract was called directly from an externally owned account (EOA).
However, on Optimism `tx.origin` is the origin *on Optimism*.
It could be an EOA.
However, in the case of messages from L1, it is possible for a message from a smart contract on L1 to appear on L2 with `tx.origin == msg.origin`.
This is unlikely to make a significant difference, because an L1 smart contract cannot directly manipulate the L2 state.
However, there could be edge cases we did not think about where this matters.
:::
### Accessing L1 information
If you need the equivalent information from the latest L1 block, you can get it from [the `L1Block` contract](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/contracts/L2/L1Block.sol).
This contract is a predeploy at address [`0x4200000000000000000000000000000000000015`](https://goerli-optimism.etherscan.io/address/0x4200000000000000000000000000000000000015).
You can use [the getter functions](https://docs.soliditylang.org/en/v0.8.12/contracts.html#getter-functions) to get these parameters:
- `number`: The latest L1 block number known to L2
- `timestamp`: The timestamp of the latest L1 block
- `basefee`: The base fee of the latest L1 block
- `hash`: The hash of the latest L1 block
- `sequenceNumber`: The number of the L2 block within the epoch (the epoch changes when there is a new L1 block)
### Address Aliasing
<details>
Because of the behavior of the `CREATE` opcode, it is possible for a user to create a contract on L1 and on L2 that share the same address but have different bytecode.
This can break trust assumptions, because one contract may be trusted and another be untrusted (see below).
To prevent this problem the behavior of the `ORIGIN` and `CALLER` opcodes (`tx.origin` and `msg.sender`) differs slightly between L1 and L2.
The value of `tx.origin` is determined as follows:
| Call source | `tx.origin` |
| ---------------------------------- | ------------------------------------------ |
| L2 user (Externally Owned Account) | The user's address (same as in Ethereum) |
| L1 user (Externally Owned Account) | The user's address (same as in Ethereum) |
| L1 contract (using `CanonicalTransactionChain.enqueue`) | `L1_contract_address + 0x1111000000000000000000000000000000001111` |
The value of `msg.sender` at the top-level (the very first contract being called) is always equal to `tx.origin`.
Therefore, if the value of `tx.origin` is affected by the rules defined above, the top-level value of `msg.sender` will also be impacted.
Note that in general, [`tx.origin` should *not* be used for authorization](https://docs.soliditylang.org/en/latest/security-considerations.html#tx-origin).
However, that is a separate issue from address aliasing because address aliasing also affects `msg.sender`.
#### Why is address aliasing an issue?
The problem with two identical source addresses (the L1 contract and the L2 contract) is that we extend trust based on the address.
It is possible that we will want to trust one of the contracts, but not the other.
1. Helena Hacker forks [Uniswap](https://uniswap.org/) to create her own exchange (on L2), called Hackswap.
**Note:** There are actually multiple contracts in Uniswap, so this explanation is a bit simplified.
[See here if you want additional details](https://ethereum.org/en/developers/tutorials/uniswap-v2-annotated-code/).
1. Helena Hacker provides Hackswap with liquidity that appears to allow for profitable arbitrage opportunities.
For example, she can make it so that you can spend 1 [DAI](https://www.coindesk.com/price/dai/)to buy 1.1 [USDT](https://www.coindesk.com/price/tether/).
Both of those coins are supposed to be worth exactly $1.
1. Nimrod Naive knows that if something looks too good to be true it probably is.
However, he checks the Hackswap contract's bytecode and verifies it is 100% identical to Uniswap.
He decides this means the contract can be trusted to behave exactly as Uniswap does.
1. Nimrod approves an allowance of 1000 DAI for the Hackswap contract.
Nimrod expects to call the swap function on Hackswap and receive back nearly 1100 USDT.
1. Before Nimrod's swap transaction is sent to the blockchain, Helena Hacker sends a transaction from an L1 contract with the same address as Hackswap.
This transaction transfers 1000 DAI from Nimrod's address to Helena Hacker's address.
If this transaction were to come from the same address as Hackswap on L2, it would be able to transfer the 1000 DAI because of the allowance Nimrod *had* to give Hackswap in the previous step to swap tokens.
Nimrod, despite his naivete, is protected because Optimism modified the transaction's `tx.origin` (which is also the initial `msg.sender`).
That transaction comes from a *different* address, one that does not have the allowance.
**Note:** It is simple to create two different contracts on the same address in different chains.
But it is nearly impossible to create two that are different by a specified amount, so Helena Hacker can't do that.
</details>
## Blocks
There are several differences in the way blocks are produced between L1 Ethereum and Optimism Bedrock.
| Parameter | L1 Ethereum | Optimism Bedrock |
| - | - | - |
| Time between blocks | 12 seconds(1) | 2 seconds |
| Block target size | 15,000,000 gas | to be determined |
| Block maximum size | 30,000,000 gas | to be determined |
(1) This is the ideal.
If any blocks are missed it could be an integer multiple such as 24 seconds, 36 seconds, etc.
**Note:** The L1 Ethereum parameter values are taken from [ethereum.org](https://ethereum.org/en/developers/docs/blocks/#block-time). The Optimism Bedrock values are taken from [the Optimism specs](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/specs/guaranteed-gas-market.md#limiting-guaranteed-gas).
## Network specifications
### JSON-RPC differences
OP Stack codebase uses the same [JSON-RPC API](https://eth.wiki/json-rpc/API) as Ethereum.
Some additional OP Stack specific methods have been introduced.
See the full list of [custom JSON-RPC methods](https://community.optimism.io/docs/developers/build/json-rpc/) for more information.
### Pre-EIP-155 support
[Pre-EIP-155](https://eips.ethereum.org/EIPS/eip-155) transactions do not have a chain ID, which means a transaction on one Ethereum blockchain can be replayed on others.
This is a security risk, so pre-EIP-155 transactions are not supported on OP Stack by default.
## Transaction costs
[By default, transaction costs on OP Stack chains](https://community.optimism.io/docs/developers/build/transaction-fees/) include an [L2 execution fee](https://community.optimism.io/docs/developers/build/transaction-fees#the-l2-execution-fee) and an [L1 data fee](https://community.optimism.io/docs/developers/build/transaction-fees#the-l1-data-fee).
---
title: Bedrock Explainer
lang: en-US
meta:
- name: og:image
content: https://dev.optimism.io/content/images/size/w2000/2022/12/bedrock-BLUE.jpg
---
![Bedrock](https://dev.optimism.io/content/images/size/w2000/2022/12/bedrock-BLUE.jpg)
::: tip Staying up to date
[Stay up to date on the Superchain and the OP Stack by subscribing to the Optimism newsletter](https://optimism.us6.list-manage.com/subscribe/post?u=9727fa8bec4011400e57cafcb&id=ca91042234&f_id=002a19e3f0).
:::
Bedrock is the name of the first ever official release of the OP Stack codebase, which is a set of free and open-source modular components that work together to power Optimism.
- To understand what is in the Bedrock release, keep reading.
- To develop on Optimism Mainnet, which will upgrade its infrastructure to the Bedrock release, read the docs.
- To contribute to the OP Stack, see the contribution guidelines on the ethereum-optimism monorepo.
## Summary of Improvements
Bedrock improves on its predecessor by reducing transaction fees using optimized batch [compression](#optimized-data-compression) and Ethereum as a data availability layer; shortening delays of including L1 transactions in rollups by handling L1 re-orgs more gracefully; enabling modular proof systems through code re-use; and improving node performance by removing technical debt.
### Lower fees
In addition, Bedrock implements an optimized data [compression](#optimized-data-compression) strategy to minimize data costs. We are currently benchmarking the impact of this change, but we expect it to reduce fees significantly.
Bedrock also removes all gas costs associated with EVM execution when submitting data to L1. This reduces fees by an additional 10% over the previous version of the protocol.
### Shorter deposit times
Bedrock introduces support for L1 re-orgs in the node software, which significantly reduces the amount of time users need to wait for deposits. Earlier versions of the protocol could take up to 10 minutes to confirm deposits. With Bedrock, we expect deposits to confirm within 3 minutes.
### Improved proof modularity
Bedrock abstracts the proof system from the OP Stack so that a rollup may use either a fault proof or validity proof (e.g., a zk-SNARK) to prove correct execution of inputs on the rollup. This abstraction enables systems like [Cannon](https://github.com/ethereum-optimism/cannon) to be used to prove faults in the system.
### Improved node performance
The node software performance has been significantly improved by enabling execution of several transactions in a single rollup "block" as opposed to the prior "one transaction per block" model in the previous version. This allows the cost of merkle trie updates to be amortized across multiple transactions. At current transaction volume, this reduces state growth by approximately 15GB/year.
Node performance is further improved by removing technical debt from the previous version of the protocol. This includes removing the need for a separate "data transport layer" node to index L1, and updating the node software to efficiently query for transaction data from L1.
### Improved Ethereum equivalence
Bedrock was designed from the ground up to be as close to Ethereum as possible. Multiple deviations from Ethereum in the previous version of the protocol have been removed, including:
1. The one-transaction-per-block model.
2. Custom opcodes to get L1 block information.
3. Separate L1/L2 fee fields in the JSON-RPC API.
4. A custom ERC20 representation of ETH balances.
Bedrock also adds support for EIP-1559, chain re-orgs, and other Ethereum features present on L1.
## Design Principles
Bedrock was built to be modular & upgradeable, to reuse existing code from Ethereum, and to be as close to 100% Ethereum-equivalent as possible.
### Modularity
Bedrock makes it easy to swap out different components in the OP Stack codebase and add new capabilities by using well-defined interfaces and versioning schemes. This allows for a flexible architecture that can adapt to future developments in the Ethereum ecosystem.
Examples:
- Separation of [rollup node](#rollup-node) and execution client
- Modular fault proof design
### Code re-use
Bedrock uses existing Ethereum architecture and infrastructure as much as possible. This approach enables the OP Stack to inherit security and "lindy" benefits from the battle-tested codebases used in production on Ethereum Mainnet. You'll find examples of this throughout the design including:
Examples:
- [Minimally modified execution clients](https://op-geth.optimism.io/)
- EVM contracts instead of precompiled client code
### Ethereum equivalence
Bedrock is designed to have maximum compatibility with the existing Ethereum developer experience. A few exceptions exist due to fundamental differences between an L1 and a rollup: an altered fee model, faster block time (2s vs 12s), and a special transaction type for including L1 [deposit](#deposits) transactions.
Examples:
- Fault proof designed to prove faults of minimally modified Ethereum [execution client](#execution-client)
- Code re-use of Ethereum [execution client](#execution-client) for use by nodes in the L2 network and sequencers
## Protocol
Rollups are derived from a data availability source (generally an L1 blockchain like Ethereum). In their most common configuration, rollup protocols derive a **"canonical L2 chain"** from two primary sources of information:
1. Transaction data posted by Sequencers to the L1 and;
2. [Deposit](#deposits) transactions posted by accounts and contracts to a bridge contract on L1.
The following are the fundamental components of the protocol:
* Deposits are _writes_ to the canonical L2 chain by directly interacting with smart contracts on the L1.
* Withdrawals are _writes_ to the canonical L2 chain that implicitly trigger interactions with contracts and accounts on the L1.
* Batches are _writes_ of data corresponding to batches on the rollup.
* Block derivation is how _reads_ of data on the L1 are interpreted to understand the canonical L2 chain.
* Proof systems define _finality_ of posted output roots on the L1 such that they may be _executed_ upon (e.g., to execute withdrawals).
### Deposits
A **deposit** is a transaction on L1 that is to be included in the rollup. [Deposits](#deposits) are _guaranteed_ by definition to be included in the [canonical L2 chain](#protocol) as a means of preventing censorship or control of the L2.
#### Arbitrary message passing from L1
A **deposited transaction** is the transaction on the rollup that is made as a part of a [deposit](#deposits). With Bedrock, [deposits](#deposits) are fully generalized Ethereum transactions. For example, an account or contract on Ethereum can “deposit” a contract creation.
Bedrock defines a **deposit contract** that is available on the L1: it is a smart contract that L1 accounts and contracts can interact with to write to the L2. [Deposited transactions](#arbitrary-message-passing-from-l1) on the L2 are derived from the values in the event(s) emitted by this [deposit](#deposits) contract, which include expected parameters such as from, to, and data.
For full details, see the [deposit contract](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/deposits.md#deposit-contract) section of the protocol specifications.
#### Purchasing guaranteed L2 gas on L1
Bedrock also specifies a gas burn mechanism and a fee market for [deposits](#deposits). The gas that [deposited transactions](#arbitrary-message-passing-from-l1) spend on an L2 is bought on L1 via a gas burn. This gas is purchased on a fee market and there is a hard cap on the amount of gas provided to all [deposits](#deposits) in a single L1 block. This mechanism is used to prevent denial of service attacks that could occur by writing transactions to L2 from L1 that are extremely gas-intensive on L2, but cheap on L1.
The gas provided to [deposited transactions](#arbitrary-message-passing-from-l1) is sometimes called "guaranteed gas." Guaranteed gas is unique in that it is paid for by burning gas on L1 and is therefore not refundable.The total amount of L1 gas that must be burned per unit of guaranteed L2 gas requested depends on the price of L2 gas reported by a EIP-1559 style fee mechanism. Furthermore, users receive a dynamic gas stipend based on the amount of L1 gas spent to compute updates to the fee mechanism.
For a deeper explanation, read the [deposits section](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/deposits.md#deposits) of the protocol specifications.
### Withdrawals
**Withdrawals** are cross-domain transactions that are initiated on L2 and finalized by a transaction executed on L1. Notably, withdrawals may be used by an L2 account to call an L1 contract, or to transfer ETH from an L2 account to an L1 account.
Withdrawals are initiated on L2 via a call to the **Message Passer** predeploy contract, which records the important properties of the message in its storage. Withdrawals are finalized on L1 via a call to the [OptimismPortal](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/withdrawals.md#the-optimism-portal-contract) contract, which proves the inclusion of this withdrawal message. In this way, withdrawals are different from [deposits](#deposits). Instead of relying on [block derivation](#block-derivation), withdrawal transactions must use smart contracts on L1 for finalization.
#### Two-step withdrawals
Withdrawal proof validation bugs have been the root cause of many of the biggest bridge hacks of the last few years. The Bedrock release introduces an additional step in the withdrawals’ process of prior versions meant to provide an extra layer of defense against these types of bugs. In the two-step withdrawal process, a Merkle proof corresponding to the withdrawal must be submitted 7 days before the withdrawal can be finalized.. This new safety mechanism gives monitoring tools a full 7 days to find and detect invalid withdrawal proofs . If the [withdrawal](#withdrawals) proof is found to be invalid, a contract fix can be deployed before funds are lost. This dramatically reduces the risk of a bridge compromise.
For full details, see the [withdrawals](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/withdrawals.md) section of the protocol specification.
### Batches
In Bedrock, a wire format is defined for messaging between the L1 and L2 (i.e., for L2 deriving blocks from L1 and for L2 to write transactions to the L1). This wire format is designed to minimize costs and software complexity for writing to the L1.
#### Optimized data compression
To optimize data compression, lists of L2 transactions called **sequencer batches** are organized into groups of objects called **channels**, each of which have a maximum size that is defined in a [configurable parameter](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/derivation.md#channel-format) that will initially be set to ~9.5Mb. These [channels](#optimized-data-compression) are expected to be compressed using a compression function and submitted to the L1.
#### Parallelized batch submission
To parallelize messages from the sequencers that are submitting [compressed](#optimized-data-compression) [channel](#optimized-data-compression) data to the L1, [channels](#optimized-data-compression) are further broken down into **channel frames**, which are chunks of [compressed](#optimized-data-compression) [channel](#optimized-data-compression) data that can fit inside of a single L1 transaction. Given [channel frames](#parallelized-batch-submission) are mutually independent and the ordering is known, the Ethereum transactions sent by the sequencer to the L1 can be sent in parallel which minimizes sequencer software complexity and allows for filling up all available space for data on the L1.
#### Minimized usage of Ethereum gas
Bedrock removes all execution gas used by the L1 system from submitting [channel](#optimized-data-compression) data to the L1 in transactions called **batcher transactions**. All validation logic that was previously happening on smart contracts on the L1 is moved into the [block derivation](#block-derivation) logic. Instead, [batcher transactions](#minimized-usage-of-ethereum-gas) are sent to a single EOA on Ethereum referred to as the **batch inbox address**.
Batches are still subject to validity checks (i.e. they have to be encoded correctly), and so are individual transactions within the batch (e.g. signatures have to be valid). Invalid [batches](#optimized-data-compression) and invalid individual transactions within an otherwise valid batch are considered to be discarded and irrelevant to the system.
> Note: Ethereum will soon upgrade to include [EIP-4844](https://eip4844.com/), which introduces a separate fee market for writing data and an increased cap of the amount of data the Ethereum protocol is willing to store. This change is expected to further decrease the costs associated with posting data to an L1.
For a deeper explanation, read [the wire format specifications](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/derivation.md#overview).
### Block Derivation
In Bedrock, the protocol is designed to guarantee that the timing of [deposits](#deposits) on the L1 is respected with regards to the [block derivation](#block-derivation) of the [canonical L2 chain](#protocol). Doing so is a _pure function_ of data written to the L1 by sequencers, [deposits](#deposits), and L1 block attributes. To accomplish this, the protocol defines strategies for guaranteeing inclusion of deposits, handling L1 and L2 timestamps, and processing sequencing windows in a pipeline to ensure correct ordering.
#### Guaranteed inclusion of deposits
A goal of the [block derivation](#block-derivation) protocol is to define it such that there must be an L2 block every "L2 block time" number of seconds, and that the timestamp of L2 blocks stays in sync with the timestamps of L1 (i.e., to ensure [deposits](#deposits) are included in a logical temporal order).
In Bedrock, the concept of a **sequencing epoch** is introduced: it is a range of L2 blocks derived from a range of L1 blocks. Each [epoch](#guaranteed-inclusion-of-deposits) is identified by an **epoch number**, which is equal to the block number of the first L1 block in the sequencing window. Epochs can vary in size, subject to some constraints.
The batch derivation pipeline treats the timestamps of the L1 blocks associated with [epoch number](#guaranteed-inclusion-of-deposits) as the anchor point for determining the order of transactions on the L2. The protocol guarantees that the first L2 block of an [epoch](#guaranteed-inclusion-of-deposits) never falls behind the timestamp of the L1 block matching the [epoch](#guaranteed-inclusion-of-deposits). The first blocks of an epoch _must_ contain deposits on L1 in order to guarantee that deposits will be processed.
Note that the target configuration for the block time on L2 in the Bedrock release is 2 seconds.
#### Handling L1 and L2 timestamps
Bedrock attempts to address the problem of reconciling the timestamps on L2 with timestamps on L1 present in [deposited transactions](#arbitrary-message-passing-from-l1). It does this by allowing a short window of time for sequencing to liberally apply timestamps on L2 transactions between [epochs](#guaranteed-inclusion-of-deposits).
A **sequencing window** is a sequence of L1 blocks from which an [epoch](#guaranteed-inclusion-of-deposits) can be derived. A [sequencing window](#handling-l1-and-l2-timestamps) whose first L1 block has the number `N` contains [batcher transactions](#minimized-usage-of-ethereum-gas) for [epoch](#guaranteed-inclusion-of-deposits) `N`.
The [sequencing window](#handling-l1-and-l2-timestamps) contains blocks `[N, N + SWS)` where `SWS` is the **sequencer window size**: a fixed rollup-level configuration parameter. This parameter must be at least 2. Increasing it provides more opportunity for sequencers to order L2 transactions with respect to [deposits](#deposits), and lowering it introduces stricter windows of time for sequencers to submit batcher transactions. It is a tradeoff between creating MEV opportunity and increasing software complexity.
A protocol constant called **max sequencer drift** governs the maximum timestamp a block can have within its epoch. Having this drift allows the sequencer to maintain liveness in case of temporary problems connecting to L1. Each L2 block’s timestamp fits within the following range:
```
l1_timestamp <= l2_block.timestamp <= max(l1_timestamp + max_sequencer_drift, l1_timestamp + l2_block_time)
```
#### Block derivation pipeline
The [canonical L2 chain](#protocol) can be processed from scratch by starting with the L2 genesis state, setting the L2 chain inception as the first epoch, and then processing all sequencing windows in order to determine the correct ordering of [sequencer batches](#optimized-data-compression) and [deposits](#deposits) according to the following simplified pipeline:
| **Stage** | **Notes** |
| --- | --- |
| Read from L1 | Epochs are defined by L1 blocks. Contained within an L2 block is data pertaining to [batcher transactions](#minimized-usage-of-ethereum-gas) or [deposits](#deposits) which must be included in the [canonical L2 chain](#protocol) |
| Buffer and decode into [channels](#optimized-data-compression) | The data from L1 blocks contains unordered [channel frames](#parallelized-batch-submission), which must all be collected before reconstructing them into channels. |
| Decompress [channels](#optimized-data-compression) into [batches](#optimized-data-compression) | Since [channels](#optimized-data-compression) are [compressed](#optimized-data-compression) to minimize data fee costs on the L1, they must be decompressed. |
| Queue [batches](#optimized-data-compression) into sequential order | With the latest information from L1, [batches](#optimized-data-compression) can be validated and processed sequentially. There are some nuances to what the correct ordering is in relation to [epochs](#guaranteed-inclusion-of-deposits) and timestamps from L2, see the full specification [here](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/derivation.md#batch-queue). |
| Interpret as L2 blocks | At this point, the correct ordering of [batches](#optimized-data-compression) can be determined.<br><br>Following this, the [execution client](#execution-client) can interpret them into L2 blocks. For implementation details pertaining to [execution clients](#execution-client), see the [engine queue](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/derivation.md#engine-queue) section of the protocol specifications. |
### Fault Proofs
After a sequencer processes one or more L2 blocks, the outputs computed from executing transactions in those blocks will need to be written with L1 for trustless execution of L2-to-L1 messaging, such as [withdrawals](#withdrawals).
In Bedrock, outputs are hashed in a tree-structured form which minimizes the cost of proving any piece of data captured by the outputs. Proposers periodically submit **output roots** that are Merkle roots of the entire [canonical L2 chain](#protocol) to the L1.
Future upgrades of the OP Stack codebase should include a specification for a variation of a fault proof with bonding included to create incentives for proposers to propose correct output roots.
For full details, read the [L2 Output Root Proposals section](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/proposals.md#l2-output-root-proposals-specification) of the protocol specifications.
## Implementation
With Bedrock, the OP Stack codebase leans heavily into the technical separation of concerns specified by Ethereum by mirroring the separation between the Ethereum execution layer and consensus layer. Bedrock introduces separation of execution client and rollup node in this same way.
### Execution Client
An **execution client** is the system that sequencers and other kinds of node operators run to determine the state of the [canonical L2 chain](#protocol). It also performs other functions such as processing inbound transactions and communicating them peer-to-peer, and handling the state of the system to process queries against it.
With Bedrock, the OP Stack is designed to reuse [Ethereum’s own execution client specifications](https://github.com/ethereum/execution-specs) and its many implementations. In this release, Bedrock has demonstrated an extremely limited modification of go-ethereum, the most popular Ethereum client written in Go, to a [diff of less than 2000 lines of code](https://op-geth.optimism.io/).
There are two fundamental reasons for having any diff at all: handling deposited transactions, and charging transaction fees.
#### Handling deposited transactions
To represent [deposited transactions](#arbitrary-message-passing-from-l1) in the rollup, there is an additional transaction type introduced. The [execution client](#execution-client) implements this [new transaction type](https://github.com/ethereum-optimism/optimism/blob/7f5b9ea7bf6dce12dbf709c27c25ee1681df7f7e/specs/deposits.md#the-deposited-transaction-type) according to the [EIP-2718 typed transactions](https://eips.ethereum.org/EIPS/eip-2718) standard.
#### Charging transaction fees
Rollups also fundamentally have two kinds of fees associated with transactions:
**Sequencer fees**
The cost of operating a sequencer is computed using the same gas table as Ethereum and with the same [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) algorithm. These fees go to the protocol for operating sequencers and fluctuate based on the congestion of the network.
**Data availability fees**
Data availability costs are associated with writing [batcher transactions](#minimized-usage-of-ethereum-gas) to the L1. These fees are intended to cover the cost that sequencers need to pay to submit [batcher transactions](#minimized-usage-of-ethereum-gas) to the L1.
In Bedrock, the data availability portion of the fee is determined based on information in a system contract on the rollup called a [GasPriceOracle](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/predeploys.md#gaspriceoracle). This contract is updated during [block derivation](#block-derivation) from the gas pricing information retrieved from the L1 block attributes that get inserted at the beginning of every [epoch](#guaranteed-inclusion-of-deposits).
Bedrock specifies that both of these fees are added up into a single `gasPrice` field when using the JSON-RPC.
### Rollup Node
Unlike Ethereum, Bedrock does not have proof-of-stake consensus. Instead, the consensus of the [canonical L2 chain](#protocol) is defined by [block derivation](#block-derivation). An [execution client](#execution-client) of the OP Stack communicates to a new component that implements [block derivation](#block-derivation) called a **rollup node**. This node communicates to the [execution client](#execution-client) using the exact same [Engine API](https://github.com/ethereum/execution-apis/tree/main/src/engine) that Ethereum uses.
The [rollup node](#rollup-node) is a stateless component responsible for deriving the state of the system by reading data and [deposits](#deposits) on the L1. In Bedrock, a [rollup node](#rollup-node) can either be used to sequence incoming transactions from users or other [rollup nodes](#rollup-node) or to verify confirmed transactions posted on the L1 by singularly relying on the L1.
The multiple uses of a rollup node are outlined below.
#### Verifying the canonical L2 chain
The simplest mode of running a [rollup node](#rollup-node) is to only follow the [canonical L2 chain](#protocol). In this mode, the [rollup node](#rollup-node) has no peers and is strictly used to read data from the L1 and to interpret it according to [block derivation](#block-derivation) protocol rules.
One purpose of this kind of node is to verify that any output roots shared by other nodes or posted on the L1 are correct according to protocol definition. Additionally, proposers intending to submit output roots to the L1 themselves can generate the output roots they need using the [optimism_outputAtBlock](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/rollup-node.md#l2-output-rpc-method) of the node which returns a 32-byte hash corresponding to the L2 output root.
For this purpose, nodes should only need to follow the finalized head. The term ["finalized"](https://ethereum.org/en/developers/docs/consensus-mechanisms/pos/#finality) refers to the Ethereum proof-of-stake consensus (i.e. canonical and practically irreversible) — the finalized L2 head is the head of the [canonical L2 chain](#protocol) that is derived only from finalized L1 blocks.
#### Participating in the L2 network
The most common way to use a [rollup node](#rollup-node) is to participate in a network of other [rollup nodes](#rollup-node) tracking the progression and state of an L2. In this mode, a [rollup node](#rollup-node) is both reading the data and [deposits](#deposits) it observes from the L1 and interpreting it as blocks and accepting inbound transactions from users and peers in a network of other [rollup nodes](#rollup-node).
Nodes participating in the network may make use of the safe and unsafe heads of the L2 they're syncing.
- The **safe L2 head** represents the rollup that can be constructed where every block up to and including the head can be fully derived from the reference L1 chain, before L1 has necessarily finalized (i.e., a re-org may occur on L1 still).
- The **unsafe L2 head** includes [unsafe blocks](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/glossary.md#unsafe-l2-block) that have not yet been derived from L1. These blocks either come from operating the [rollup node](#rollup-node) as a sequencer or from [unsafe sync](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/glossary.md#unsafe-sync) with the sequencer. This is also known as the "latest" head. The safe L2 head is always chosen over the unsafe L2 head in cases of disagreements. When disagreements occur, the unsafe portion of the chain will reorg.
For most purposes, nodes in the L2 network will refer to the unsafe L2 head for end-user applications.
#### Sequencing transactions
The third way to use a [rollup node](#rollup-node) is to sequence transactions. In this mode, a [rollup node](#rollup-node) will _create_ new blocks on top of the unsafe L2 head. Currently, there is only one sequencer per OP Stack network.
The sequencer is also responsible for posting batches to L1 for other nodes in the network to sync from.
### Batcher
The role of a sequencer is to produce [batches](#batches). To do this, a sequencer can run [rollup nodes](#rollup-node) and have separate processes which perform [batching](#batches) by reading from a trusted [rollup node](#rollup-node) they run. This warrants an additional component of the OP Stack called a [batcher](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/glossary.md#batcher) that reads transaction data from a [rollup node](#rollup-node) and interprets it into [batcher transactions](#minimized-usage-of-ethereum-gas) to be written to the L1. The batcher component is responsible for reading the unsafe L2 head of a [rollup node](#rollup-node) run by a sequencer, creating batcher transactions, and writing them to the L1.
### Standard Bridge Contracts
Bedrock also includes a pair of bridge contracts used for the most common kinds of [deposits](#deposits) called the [standard bridges](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/bridges.md#standard-bridges). These contracts wrap the [deposit](#deposits) and [withdrawal](#withdrawals) contracts to provide simple interfaces for [depositing](#deposits) and [withdrawing](#withdrawals) ETH and ERC-20 tokens.
These bridges are designed to involve a native token on one side of the bridge, and a wrapped token on the other side that can manage minting and burning. Bridging a native token involves locking the native token in a contract and then minting an equivalent amount of mintable token on the other side of the bridge.
For full details, see the [standard bridge](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/bridges.md#standard-bridges) section of the protocol specifications.
### Cannon
Although fault proof construction and verification is implemented in the [Cannon](https://github.com/ethereum-optimism/cannon) project, the fault proof game specification and integration of an output root challenger into the rollup node are part of later specification milestones.
## Further Reading
### Protocol Specification
The protocol specification defines the technical details of the OP Stack codebase. It is the most up-to-date source of truth for the inner workings of the protocol. The protocol specification is located in the ethereum-optimism [monorepo](https://github.com/ethereum-optimism/optimism/blob/d69cb12f6dcbe3d5355beca8997fbac611b7fe37/specs/README.md).
### Bedrock Differences
For a deep dive into the differences between Bedrock and previous versions of the protocol, see the [How is Bedrock Different?](https://community.optimism.io/docs/developers/bedrock/differences/) page.
---
title: Security FAQs
lang: en-US
---
::: warning 🚧 Work in Progress
The OP Stack is a work in progress. Constantly pushing to improve the overall security and decentralization of the OP Stack is a top priority.
:::
## Security in the decentralized context
The OP Stack is a decentralized development stack that powers Optimism. Components of the OP Stack may be maintained by various different teams within the Optimism Collective. It is generally easier to talk about the security model of specific chains built on the OP Stack rather than the security model of the stack itself. **The OP Stack security baseline is to create safe defaults while still giving developers the flexibility to make modifications and extend the stack.**
## FAQ
### Is every OP Stack chain safe?
The security model of an OP Stack based blockchain depends on the modules used for its components. Because of the flexibility provided by OP Stack, it is always possible to set up an insecure blockchain using OP Stack components. **The goal of the OP Stack is to provide safe defaults.**
Please also keep in mind that just like any other system, **the OP Stack may contain unknown bugs** that could lead to the loss of some or all of the assets held within an OP Stack based system. [Many components of the OP Stack codebase have been audited](https://github.com/ethereum-optimism/optimism/tree/129032f15b76b0d2a940443a39433de931a97a44/technical-documents/security-reviews) but **audits are not a stamp of approval** and **a completed audit does not mean that the audited codebase is free of bugs.** It’s important to understand that using the OP Stack inherently exposes you to the risk of bugs within the OP Stack codebase.
### Is the OP Stack safe to modify?
As with anything, modify the OP Stack at your own risk. There is no guarantee that modifications to the stack will be safe. If you aren’t entirely sure about what you’re doing, stick with the safer defaults that the OP Stack provides. At the moment, the OP Stack is not particularly amenable to modifications and **you should not expect any technical support for modifications that fall outside of the standard Rollup configuration of the stack**.
### Can I use fault proofs?
**Not yet.** The OP Stack does not currently have a fault proof system. **Note that fault proofs do not meaningfully improve the security of a system if that system can be upgraded within the 7 day challenge window (”fast upgrade keys”)**. A system with fast upgrade keys is fully dependent on the upgrade keys for security.
Fault proofs are a key milestone and top priority for the OP Stack. In the meantime, the OP Stack can be shipped with several other excellent security options for systems that want to improve security before fault proofs are available in production.
### How can I help make the OP Stack more secure?
One of the easiest ways to help secure the OP Stack is to look for bugs and vulnerabilities. [Optimism Mainnet, a user of the OP Stack, has one of the biggest bug bounties (ever)](https://immunefi.com/bounty/optimism/). You can earn up to $2,000,042 by finding critical bugs in the Optimism Mainnet codebase (and by extension the OP Stack).
Don’t forget that the OP Stack is a decentralized development stack. Anyone can start to contribute to the OP Stack by building software that follows [the stack’s design principles](../understand/design-principles.md). You can always help make the OP Stack more secure by building components, like alternative client or proof implementations, that users of the OP Stack can take advantage of.
### Where do I report bugs?
[View the Security Policy for details about reporting vulnerabilities and available bug bounty programs](./policy.md)
\ No newline at end of file
README.md
\ No newline at end of file
---
title: Forced withdrawal from an OP Stack blockchain
lang: en-US
---
## What is this?
Any assets you own on an OP Stack blockchain are backed by equivalent assets on the underlying L1, locked in a bridge.
In this article you learn how to withdraw these assets directly from L1.
Note that the steps here do require access to an L2 endpoint.
However, that L2 endpoint can be a read-only replica.
## Setup
The code to go along with this article is available at [our tutorials repository](https://github.com/ethereum-optimism/optimism-tutorial/tree/main/op-stack/forced-withdrawal).
1. Clone the repository, move to the correct directory, and install the required dependencies.
```sh
git clone https://github.com/ethereum-optimism/optimism-tutorial.git
cd optimism-tutorial/op-stack/forced-withdrawal
npm install
```
1. Copy the environment setup variables.
```sh
cp .env.example .env
```
1. Edit `.env` to set these variables:
| Variable | Meaning |
| -------------------- | ------- |
| L1URL | URL to L1 (Goerli if you followed the directions on this site)
| L2URL | URL to the L2 from which you are withdrawing
| PRIV_KEY | Private key for an account that has ETH on L2. It also needs ETH on L1 to submit transactions
| OPTIMISM_PORTAL_ADDR | Address of the `OptimismPortalProxy` on L1.
## Withdrawal
### ETH withdrawals
The easiest way to withdraw ETH is to send it to the bridge, or the cross domain messenger, on L2.
1. Enter the Hardhat console.
```sh
npx hardhat console --network l1
```
1. Specify the amount of ETH you want to transfer.
This code transfers one hundred'th of an ETH.
```js
transferAmt = BigInt(0.01 * 1e18)
```
1. Create a contract object for the [`OptimismPortal`](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol) contract.
```js
optimismContracts = require("@eth-optimism/contracts-bedrock")
optimismPortalData = optimismContracts.getContractDefinition("OptimismPortal")
optimismPortal = new ethers.Contract(process.env.OPTIMISM_PORTAL_ADDR, optimismPortalData.abi, await ethers.getSigner())
```
1. Send the transaction.
```js
txn = await optimismPortal.depositTransaction(
optimismContracts.predeploys.L2StandardBridge,
transferAmt,
1e6, false, []
)
rcpt = await txn.wait()
```
1. To [prove](https://sdk.optimism.io/classes/crosschainmessenger#proveMessage-2) and [finalize](https://sdk.optimism.io/classes/crosschainmessenger#finalizeMessage-2) the message we need the hash.
Optimism's [core-utils package](https://www.npmjs.com/package/@eth-optimism/core-utils) has the necessary function.
```js
optimismCoreUtils = require("@eth-optimism/core-utils")
withdrawalData = new optimismCoreUtils.DepositTx({
from: (await ethers.getSigner()).address,
to: optimismContracts.predeploys.L2StandardBridge,
mint: 0,
value: ethers.BigNumber.from(transferAmt),
gas: 1e6,
isSystemTransaction: false,
data: "",
domain: optimismCoreUtils.SourceHashDomain.UserDeposit,
l1BlockHash: rcpt.blockHash,
logIndex: rcpt.logs[0].logIndex,
})
withdrawalHash = withdrawalData.hash()
```
1. Create the object for the L1 contracts, [as explained in the documentation](../build/sdk.md).
You will create an object similar to this one:
```js
L1Contracts = {
StateCommitmentChain: '0x0000000000000000000000000000000000000000',
CanonicalTransactionChain: '0x0000000000000000000000000000000000000000',
BondManager: '0x0000000000000000000000000000000000000000',
AddressManager: '0x432d810484AdD7454ddb3b5311f0Ac2E95CeceA8',
L1CrossDomainMessenger: '0x27E8cBC25C0Aa2C831a356bbCcc91f4e7c48EeeE',
L1StandardBridge: '0x154EaA56f8cB658bcD5d4b9701e1483A414A14Df',
OptimismPortal: '0x4AD19e14C1FD57986dae669BE4ee9C904431572C',
L2OutputOracle: '0x65B41B7A2550140f57b603472686D743B4b940dB'
}
```
1. Create the data structure for the standard bridge.
```js
bridges = {
Standard: {
l1Bridge: l1Contracts.L1StandardBridge,
l2Bridge: "0x4200000000000000000000000000000000000010",
Adapter: optimismSDK.StandardBridgeAdapter
},
ETH: {
l1Bridge: l1Contracts.L1StandardBridge,
l2Bridge: "0x4200000000000000000000000000000000000010",
Adapter: optimismSDK.ETHBridgeAdapter
}
}
```
1. Create [a cross domain messenger](https://sdk.optimism.io/classes/crosschainmessenger).
This step, and subsequent ETH withdrawal steps, are explained in [this tutorial](https://github.com/ethereum-optimism/optimism-tutorial/tree/main/cross-dom-bridge-eth).
```js
optimismSDK = require("@eth-optimism/sdk")
l2Provider = new ethers.providers.JsonRpcProvider(process.env.L2URL)
await l2Provider._networkPromise
crossChainMessenger = new optimismSDK.CrossChainMessenger({
l1ChainId: ethers.provider.network.chainId,
l2ChainId: l2Provider.network.chainId,
l1SignerOrProvider: await ethers.getSigner(),
l2SignerOrProvider: l2Provider,
bedrock: true,
contracts: {
l1: l1Contracts
},
bridges: bridges
})
```
1. Wait for the message status for the withdrawal to become `READY_TO_PROVE`.
By default the state root is written every four minutes, so you're likely to need to need to wait.
```js
await crossChainMessenger.waitForMessageStatus(withdrawalHash,
optimismSDK.MessageStatus.READY_TO_PROVE)
```
1. Submit the withdrawal proof.
```js
await crossChainMessenger.proveMessage(withdrawalHash)
```
1. Wait for the message status for the withdrawal to become `READY_FOR_RELAY`.
This waits the challenge period (7 days in production, but a lot less on test networks).
```js
await crossChainMessenger.waitForMessageStatus(withdrawalHash,
optimismSDK.MessageStatus.READY_FOR_RELAY)
```
1. Finalize the withdrawal.
See that your balance changes by the withdrawal amount.
```js
myAddr = (await ethers.getSigner()).address
balance0 = await ethers.provider.getBalance(myAddr)
finalTxn = await crossChainMessenger.finalizeMessage(withdrawalHash)
finalRcpt = await finalTxn.wait()
balance1 = await ethers.provider.getBalance(myAddr)
withdrawnAmt = BigInt(balance1)-BigInt(balance0)
```
::: tip transferAmt > withdrawnAmt
Your L1 balance doesn't increase by the entire `transferAmt` because of the cost of `crossChainMessenger.finalizeMessage`, which submits a transaction.
:::
\ No newline at end of file
---
title: Pause and Unpause the Bridge
lang: en-US
---
## Why do it?
The `OptimismPortal` is a bridge contract that makes it possible to send messages between your L1 and your L2 OP Stack chain.
The `OptimismPortal` is pausable as a backup safety mechanism that allows a specific `GUARDIAN` address to temporarily halt deposits and withdrawals to mitigate security issues if necessary.
An OP Stack chain does not have to specify a usable `GUARDIAN` address if it does not want to make the `OptimismPortal` contract pausable, it can specify an address such as zero.
## Who can do it?
[`OptimismPortal`](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol) has an immutable `GUARDIAN`.
That address can call [`pause`](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L171-L178) and [`unpause`](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/contracts/L1/OptimismPortal.sol#L180-L187).
### Changing the guardian
The guardian created by the setup script is the admin account.
This is sufficient for testing, but for a production system you would want the guardian to be a multisig with trusted security council.
The `GUARDIAN` variable is immutable, but the `OptimismPortal` contract sits behind a proxy, so the `GUARDIAN` can be modified by changing the `OptimismPortal` proxy to point to a new implementation contract.
You do this using the L1 [`ProxyAdmin`](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/contracts/universal/ProxyAdmin.sol) contract.
<!--
## Seeing it in action
1. Set these environment variables
| Variable | Meaning
| - | - |
| `PRIV_KEY` | Private key for your ADMIN account
| `ADMIN_ADDR` | Address of the ADMIN account
| `PORTAL_ADDR` | Portal proxy address, get from `.../optimism/packages/contracts-bedrock/deployments/getting-started/OptimismPortalProxy.json`
| `GOERLI_RPC` | URL for an RPC to the L1 Goerli network
1. For using Foundry, set `ETH_RPC_URL`.
```sh
export ETH_RPC_URL=$GOERLI_RPC
```
1. Check the balance of the ADMIN account.
If it is too low you will not be able to submit transactions.
```sh
cast balance $ADMIN_ADDR
```
1. Send a deposit to L2.
```sh
cast send --private-key $PRIV_KEY --value 1gwei $PORTAL_ADDR
```
Note the transaction hash.
1. Pause the portal.
```sh
cast send --private-key $PRIV_KEY $PORTAL_ADDR "pause()"
```
1. Send a deposit to L2.
```sh
cast send --private-key $PRIV_KEY --value 1gwei $PORTAL_ADDR
```
Note the transaction hash.
1. Wait ten minutes and see which transaction(s) have been relayed using the [SDK](../build/sdk.md).
Use [`getMessageStatus`](https://sdk.optimism.io/classes/crosschainmessenger#getMessageStatus) to get the information.
1. Unpause the portal.
```sh
cast send --private-key $PRIV_KEY $PORTAL_ADDR "pause()"
```
-->
---
title: Security Policy, Vulnerability Reporting, and Bug Bounties
lang: en-US
---
## Reporting in the decentralized context
It's important to remember that the OP Stack is a decentralized software development stack built by the Optimism Collective. Different components of the OP Stack may be maintained by different teams that have different reporting processes. **This page describes general best practices for reporting bugs and provides specific reporting guidelines for the OP Stack code contained within the [ethereum-optimism](https://github.com/ethereum-optimism) GitHub organization**.
## Reporting bugs and vulnerabilities
::: danger 🚫 How NOT to disclose a vulnerability
Do *not* disclose vulnerabilities publicly or by executing them against a production network. If you do, will you not only be putting users at risk, but you will forfeit your right to a reward. Always follow the appropriate reporting pathways as described below.
- Do *not* disclose the vulnerability publicly, for example by filing a public ticket.
- Do *not* test the vulnerability on a publicly available network, either the testnet or the mainnet.
:::
### OP Stack bounty programs
The security of OP Stack smart contracts and blockchain infrastructure is paramount. Below are the various OP Stack-related bug bounty programs, as well as how to reach out if your bug is not covered by an existing bounty.
#### Optimism Mainnet bounty program
Optimism Mainnet is covered by a comprehensive [bug bounty program on Immunefi](https://immunefi.com/bounty/optimism/), which has already resulted in one of the [largest bounty payouts ever](https://medium.com/ethereum-optimism/disclosure-fixing-a-critical-bug-in-optimisms-geth-fork-a836ebdf7c94). In the listing you can find all the information relating to assets in scope, reporting, and the payout process. Because Optimism Mainnet is currently the primary user of the OP Stack, bugs in OP Stack software can generally be reported via the Optimism Mainnet bounty program.
#### Unscoped bugs
If you think you have found a significant bug or vulnerabilities in OP Stack smart contracts, infrastructure, etc., even if that component is not covered by an existing bug bounty, please report it to via the [Optimism Mainnet Immunefi program](https://immunefi.com/bounty/optimism/). The impact of any and all reported issues will be considered and the program has previously rewarded security researchers for bugs not within its stated scope.
### Other vulnerabilities
For vulnerabilities in any websites, email servers, or other non-critical infrastructure within the OP Stack, please email [OP Labs](https://www.oplabs.co/) at [security@oplabs.co](mailto:security@oplabs.co) and include detailed instructions for confirming and reproducing the vulnerability.
## Vulnerability disclosure
Each OP Stack component maintainer may determine its own process for vulnerability disclosure. However, the following describes a recommended process for disclosure that is currently in use by [OP Labs](https://www.oplabs.co/).
In the event that an OP Stack component maintainer learns of a critical security vulnerability, the maintainer reserves the right to silently fix it without immediately publicly disclosing the existence of nature of the vulnerability.
In such a scenario, the disclosure process used by [OP Labs](https://www.oplabs.co/) is as follows:
1. Silently fix the vulnerability and include the fix in release X.
1. After 4-8 weeks, disclose that release X contained a security fix.
1. After an additional 4-8 weeks, publish details of the vulnerability, along with credit to the reporter (with express permission from the reporter).
Alongside this policy, maintainers also reserve the right to:
- Bypass this policy and publish details on a shorter timeline.
- Directly notify a subset of downstream users prior to making a public announcement.
This policy is based the [Geth](https://geth.ethereum.org/) team’s [silent patch policy](https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities#why-silent-patches).
\ No newline at end of file
---
title: Design Principles for USEful Software
lang: en-US
---
::: tip The OP Stack is USEful software
The OP Stack is a set of software components for building L2 blockchain ecosystems, built by the Optimism Collective to power Optimism.
Components to be added to the OP Stack should be built according to three key design principles:
- **U**tility
- **S**implicity
- **E**xtensibility.
Software that follows these principles is **USE**ful software for the Optimism Collective!
:::
## Utility
For something to be part of the OP Stack, it should help power the Optimism Collective.
This condition helps guide the type of software that can be included in the stack.
For instance, a powerful open-source block explorer that makes it easier for users to inspect [the Superchain](https://app.optimism.io/superchain/) would be a great addition to the OP Stack.
Although utility is important for inclusion in the OP Stack, you shouldn’t be afraid to experiment.
Do something crazy.
Build something that’s never been built before, even if it doesn’t have any clear utility. Make a blockchain for Emojis, or whatever. Have fun!
## Simplicity
Complex code does not scale.
Code that makes it into the OP Stack should be simple.
Simplicity reduces engineering overhead, which in turn means the Collective can spend its time working on new features instead of re-creating existing ones.
The OP Stack prefers to use existing battle-tested code and infrastructure where possible.
The most visible example of this philosophy in practice is the choice to use Geth as the OP Stack’s default execution engine.
When dealing with critical infrastructure, simplicity is also security and maintainability.
Every line of code written is an opportunity to introduce bugs and vulnerabilities.
A simple protocol means there's less code to write and, as a result, less surface area for potential mistakes.
A clean and minimal codebase is also more accessible to external contributors and auditors.
All of this serves to maximize the security and correctness of the OP Stack.
## Extensibility
Good OP Stack code is inherently open, collaborative, and extensible.
Collaboration allows us to break out of siloed development.
Collaboration allows us spend more time building on top of one another's work and less time rebuilding the same components over and over again.
Collaboration is how we win, *together*.
Extensible code should be designed with the mindset that others will want to build with and on top of that code.
In practice, this means that the code should be open source (under a permissive license), expose clean APIs, and generally be modular such that another developer can relatively easily extend the functionality of the code.
Extensibility is a key design principle that unlocks the superpower of collaboration within the Optimism Collective ecosystem.
## Contributing to the OP Stack
The OP Stack is a decentralized software stack that anyone can contribute to.
If you're interested in contributing to the OP Stack, check out [the Contributing section of these docs](../contribute.md).
Of course, software that has impact for the Optimism Collective can receive [Retroactive Public Goods Funding](https://app.optimism.io/retropgf).
Build for the OP Stack — get rewarded for writing great open source software. What's not to love?
---
title: Superchain Explainer
lang: en-US
image: /assets/logos/twitter-superchain.png
meta:
- name: twitter:image
content: https://stack.optimism.io/assets/logos/twitter-superchain.png
- property: og:image
content: https://stack.optimism.io/assets/logos/twitter-superchain.png
- name: twitter:title
content: Superchain Explainer
- property: og:title
content: Superchain Explainer
- name: twitter:card
content: summary_large_image
---
::: tip Staying up to date
[Stay up to date on the Superchain and the OP Stack by subscribing to the Optimism newsletter](https://optimism.us6.list-manage.com/subscribe/post?u=9727fa8bec4011400e57cafcb&id=ca91042234&f_id=002a19e3f0).
:::
The next major scalability improvement to the OP Stack after [Bedrock](../releases/bedrock/) is to introduce the concept of *a Superchain*: a network of chains that share bridging, decentralized governance, upgrades, a communication layer and more—all built on the OP Stack.
The launch of the Superchain would merge Optimism Mainnet and other chains into a single unified network of OP Chains (i.e., chains within the Superchain), and mark a major step towards bringing scalable and decentralized compute to the world. The goal of this document is to describe the scalability vision, the Superchain concept, and some changes to the OP Stack required to make this vision a reality.
This is the detailed explanation. [Click here for a less technical introduction](https://app.optimism.io/superchain/).
::: tip Note
Today, the Superchain is a concept and in-flight project, not a concrete reality. This documentation represents our best current guess as to what the Superchain’s components, features, and roadmap will be. Ultimately, its actualization will depend on (and change alongside) contributions from across the entire Optimism Collective. We cannot wait to see where it goes.
:::
## The Scalability Vision
### Blockchain tech today is insufficient for the decentralized web
The unfortunate truth is that the blockchain ecosystem has not realized the potential of creating the decentralized web, a re-architected internet where trusted entities are replaced by permissionless protocols. This is largely due to the fact that a majority of web applications are unable to be run onchain due to scalability constraints inherent to the current state of blockchain technology—a problem which has plagued the industry since its inception.
In fact, in a display of remarkable foresight, the very first response to the Bitcoin whitepaper was:
> **We very, very much need such a system, but the way I understand your proposal, it does not seem to scale to the required size.**
More than a decade later this has not changed.
### The value of scalable decentralized compute is immense…
Imagine a world where we solved the blockchain scalability problem. Imagine if transacting onchain would be as cheap as interacting with centralized backends. In this world, what would be possible?
- Developers wouldn’t need to worry about the backend infrastructure their app exists on because the chain guarantees correct execution, uptime, and [horizontal scalability](https://en.wikipedia.org/wiki/Scalability#Horizontal_(scale_out)_and_vertical_scaling_(scale_up)) of their app.
- Due to the shared smart contract execution environment, composability would be supercharged far beyond the capabilities of traditional REST APIs.
- With standardized gas markets, developers are not required to front all infrastructure costs for their users. Paying for a viral application would no longer be a barrier to entry for app devs, and more monetization strategies would be unlocked.
The combination of these features would make it possible to program highly scalable web applications without having to touch the traditional backend software stack. Removing the need to worry about backends is a value proposition which extends beyond decentralization enthusiasts into regular application developers who just want to ship a product. With scalability, blockchains can go from a niche interest to becoming a core component of every developer’s toolkit.
Additionally, in this world where most applications go onchain more data becomes cryptographically verifiable. This cryptographic verifiability enables users to build reputations which transfer across all of their applications. The reputation can then be used for voting, loans, and collateral—facilitating trust on the internet. Plus there is no risk of losing access because users retain ownership of their data, applications, and reputation.
There is no doubt that the promise of blockchains could change the internet as we know it.
### …and the decentralized web can still be realized
This hypothetical isn’t a dream, it’s a tangible vision for the future which has motivated many—including Optimism—to dedicate their lives to its pursuit. Due to these collective contributions, every year we learn more about the blockchain technology stack and get closer to realizing the vision.
With the support of the industry, we think a clear picture for how to architect a truly scalable blockchain is beginning to come into view. We call it the “Superchain”. This document lays out the core technical principles underlying the Superchain architecture, as well as a set of tangible projects which, when complete, we believe will finally realize the blockchain scalability vision. It will be a multi-year (if not decade) journey. However, if we know roughly where we’re going we’ll get there a little faster.
## Foundational Superchain Concepts
### Horizontal scalability requires multiple chains…
Horizontal blockchain scalability fundamentally requires multiple chains. This is because the hardware requirements to sync a chain increase linearly with the amount of compute the chain performs. Therefore, to achieve horizontal scalability we must run chains in parallel.
::: details Chain
A state [transition system](https://en.wikipedia.org/wiki/Transition_system)—consisting of an initial state, a state transition function, and a list of inputs (transactions)—which is cryptographically committed to and can be independently replicated with commodity computer hardware and internet connection.
:::
### …but traditional multi-chain architectures are insufficient
Traditional approaches to ‘multi-chain’ architectures suffer from two fundamental problems:
1. Each chain introduces a new security model, resulting in compounding systemic risk as new chains are introduced into the ecosystem. (related [link](https://twitter.com/VitalikButerin/status/1479501366192132099?s=20))
2. New chains are costly to spin up because they require new validator sets & block producers.
These issues come from a lack of a single shared blockchain (an “L1” chain) which serves as a shared source of truth for all of the chains (”L2” chains) within the multi-chain system. By using the shared source of truth it is possible to: a) enforce standard security models across all chains; and b) remove the requirement that chain deployments require a new set of validators because each L2 chain uses L1 consensus.
### Not multi-chain, not mono-chain… Superchain
By using L2 chains to comprise the multi-chain ecosystem, it becomes possible to begin to treat chains as commodities—interchangeable compute resources. This commodification of chains enables developers to build cross-chain applications without introducing systemic risk and without incurring large overhead as new chains are deployed for their application. The concept of a chain itself can become abstracted, and at this point it will become possible to treat this network of interoperable chains as a single unit: the Superchain.
::: details Superchain
A decentralized blockchain platform which consists of many chains that share security and a technology stack (OP Stack). The interoperability and standardization enables individual chains to be treated identically by tools and wallets.
:::
## Superchain Overview
### The Superchain at a glance
The Superchain is a network of L2 chains, known as OP Chains, which share security, a communication layer, and an open source technology stack. However, unlike multi-chain designs, these chains are standardized and intended to be used as interchangeable resources. This enables developers to build applications which target the Superchain as a whole, and abstract away the underlying chains the apps are running on.
::: details OP Chain
An individual chain within the Optimism Superchain. All chains, regardless of their specific properties are considered OP Chains if they are officially governed by the Optimism Collective, and therefore part of the Superchain.
:::
![Superchain Explainer Diagram.png](../../assets/docs/understand/superchain-diag.png)
### Properties of the Superchain
In order for Optimism to upgraded to a Superchain, it must have the following properties:
| Property | Purpose |
| - | - |
| Shared L1 blockchain | Provides a total ordering of transactions across all OP Chains.
| Shared bridge for all OP Chains | Enables OP Chains to have standardized security properties.
| Cheap OP Chain deployment | Enables deploying and transacting on OP Chains without the high fees of transacting on L1.
| Configuration options for OP Chains | Enables OP Chains to configure their data availability provider, sequencer address, etc.
| Secure transactions and cross-chain messages | Enables users to safely migrate assets between OP Chains.
Once Optimism has satisfied these properties it may be considered a Superchain.
## Upgrading Optimism to Become a Superchain
We believe the following changes (after the Bedrock release) are required to create a initial Superchain that makes it possible to deploy and upgrade many chains with the same bridge:
### Upgrade the Bedrock bridge to be a chain factory
Bedrock introduced the [SystemConfig contract](https://github.com/ethereum-optimism/optimism/blob/74a63c94d881442b4edd4df6492513e0113eb064/packages/contracts-bedrock/contracts/L1/SystemConfig.sol) which began to define the L2 chain directly with L1 smart contracts. This can be extended to put *all information* defining the L2 chain, onchain. Including generating a unique chain ID, key configuration values such as block gas limit, etc.
Once the chain data is entirely onchain, we can create a factory which deploys the configuration and all other required contracts for each chain. This can be extended further by making the contract addresses deterministic with CREATE2, meaning that given a chain config it is possible to determine all bridge addresses associated with that chain. This also enables chains to be interacted with without having to deploy their bridge contracts, making (counterfactual) chain deployment virtually free, and allowing chains to inherit standard security properties.
### Derive OP Chain data using the chain factory
[Bedrock introduced L2 chain derivation from an L1 chain](../releases/bedrock/explainer/#block-derivation), where all chain data can be synced based on L1 blocks. With the L1 chain factory extending this to put all configuration onchain, it should become possible for Optimism nodes to sync *any* OP Chain deterministically given a single L1 address plus a connection to L1.
::: tip 📌
When the OP Chain is synced, the chain state is locally computed. This means determining the state of the OP Chain is fully permissionless & secure. No proof system is required for chain derivation because all invalid transactions are simply ignored by the local computation process performed by the node. A proof system is, however, still required to enable Superchain withdrawals.
:::
### Permissionless proof system to enable withdrawals
In Bedrock, there is a permissioned entity (the proposer) who is required for users to submit withdrawals. Additionally, proposers must submit proposals to L1 at a set interval. This introduces linear overhead as the number of chains in the Superchain increases, and even introduces an upper bound on the number of chains due to the limited L1 resources.
In order to address these issues, we can introduce two features:
1. Withdrawal claims (a.k.a. Permissionless proposals) — allow anyone to submit a withdrawal (aka a proposal), not just a designated proposer. This removes the permissioned entity from the system, enabling users to submit their own withdrawal messages.
2. Remove proposal submission interval — enable withdrawal claims to be made *only* when a user needs to withdraw. This removes the overhead incurred when deploying a new OP Chain.
::: details Withdrawal claims
A claim about the state of one chain made on another chain. For instance, I can claim that in OP Mainnet I have burned my tokens with the intent to withdraw those tokens back to L1.
:::
We can enable these two features first by introducing a permissionless proof system to the Optimism bridge contracts. With the modular proof design introduced in Bedrock, proofs may come in the form of fault proofs or validity proofs (e.g. zero knowledge proofs). However, until validity proofs are productionized, we assume withdrawals will use a fault proof system.
In the envisioned fault proof system, anyone can submit a withdrawal claim, and these withdrawal claims can be submitted at any time. Submitting withdrawal claims can be permissionless when claims come with bonds attached to them, as these bonds act as collateral if the claim is proven to be invalid. If a challenger successfully challenges the claim, the bond is paid out to the challenger for their participation in securing the system, thereby preventing spam even within this permissionless system. Additionally, there is no need to submit them at a regular interval because the fault proof game can efficiently prove the entire history of the chain since genesis.
The fault proof implementation may initially rely on a trusted set of chain attestors to be the final arbiter of disputes. Challengers must request attestations from a large number of chain attestors and combine these attestations into a single transaction called an attestation proof. The attestation proof is then used to challenge invalid claims.
The attestation-based fault proof should be designed to prefer safety over liveness. That means that if these chain attestors are malicious they cannot alone break the safety of withdrawals. The worst failure they can cause is preventing withdrawals from being processed until the next upgrade—a liveness failure.
In the future, the attestation proof will be incrementally phased out and replaced with trust-minimized proofs such as the [Cannon proof system](https://github.com/ethereum-optimism/cannon).
### Configurable sequencer per OP Chain
Bedrock introduced the ability to set the sequencer address in the SystemConfig contract. As we introduce multiple chains with their own SystemConfig contracts, we can enable the sequencer address to be configured by the OP Chain deployer. We call this configurable sequencer design modular sequencing. This enables OP Chains to be sequenced by different entities while retaining the standard [Superchain bridge] security model—a critical step towards sequencer decentralization.
::: details Modular sequencing
The ability to configure the sequencer address during OP Chain deployment. This value can be configured by the OP Chain deployer.
:::
::: details Superchain bridge
The L1 bridge contracts which govern all OP Chains in the Superchain. This bridge can be upgraded by the Optimism Collective.
:::
Within the Superchain bridge security model, chain safety (i.e. validity) as well as chain liveness (i.e. censorship resistance) is guaranteed. Safety is guaranteed by the proof system, and liveness is guaranteed by the ability to submit [transactions directly to L1](../releases/bedrock/explainer/#deposits). The combination of safety and liveness means that if an OP Chain sequencer were to misbehave, users can always submit transactions to L1 that migrates their usage to a new OP Chain with a correctly functioning sequencer.
Modular sequencing also enables permissionless experimentation with different sequencing models. Developers can envision implementing sequencing protocols such as: round robin sequencing, sequencer consensus protocols, PGA ordering, or FIFO ordering. We can expect that over time user friendly sequencing standards will emerge from the competition between competing sequencing protocols.
### One shared upgrade path for all OP Chains
To ship the initial Superchain with high confidence in security and decentralization, a decentralized security council should be introduced to govern upgrades. The security council should be able to update the set of chain attestors, initiate contract upgrades with a delay, and hit an emergency bridge pause button which also cancels pending upgrades.
The ability to pause the bridge in case of emergency means that in the worst case, where the requisite threshold of the security council participants had their private keys leaked, the result would be that withdrawals are indefinitely paused and bridge upgrades would be perpetually canceled. In other words, the L1 funds would be frozen. This follows the design principle of safety over liveness—the principle that one should always prevent the loss of funds (i.e. enforce safety) even if it means the funds get locked (i.e. sacrifice liveness).
#### Unfreezing the bridge via L1 soft fork
In order to address the frozen funds, there is a potential final recovery mechanism which has been discussed by the L2 community which we call the “L1 Soft Fork Upgrade Recovery” mechanism. This mechanism enables L1 to initiate a bridge upgrade with a soft fork, bypassing all other permissions within the Superchain bridge contracts. This approach may [introduce systemic risk](https://vitalik.ca/general/2023/05/21/dont_overload.html) to Ethereum and requires research and community buy-in before implementation. It is not required for implementing the Superchain and is being documented for research completeness. Without further research into the implications and safety, it is not an approach the team currently endorses.
The mechanism is as follows:
*Anyone* may propose an upgrade by submitting a transaction to a special bridge contract, along with a very large bond. This begins a two week challenge period. During this challenge period, anyone may submit a challenge which immediately cancels the upgrade and claims the bond. Under normal circumstances, it is impossible that an upgrade would go uncancelled for the required two weeks due to the large incentive provided for anyone to cancel the upgrade. However, if the upgrade is accompanied by a modification to Ethereum L1 validator software (the L1 soft fork), which ignores blocks that contain the cancellation transaction then it may succeed.
While a successful upgrade of this type would represent a soft fork of Ethereum L1, it would not incur long term technical debt to the Ethereum codebase because the soft fork logic can be removed once the upgrade has completed.
We expect this escape hatch will never be used, but its very existence could deter malicious behavior.
### The combination of these features results in a system satisfying the core Superchain properties
We believe these upgrades can provide a shared bridge for all OP Chains, cheap OP Chain deployment, important configuration options for the OP Chains, as well as secure transactions and cross-chain messages. Because the Bedrock release already provides the property of a shared L1 blockchain, after these changes we will have achieved all of the core properties required for the Superchain.
## Extending the Superchain—enhancements to realize the vision
We expect that, if successful, the post-Bedrock Superchain release will mark a major milestone in the scalability and decentralization of Optimism. However, there will still be significant pain points which must be addressed before the full scalable blockchain vision has been realized. Anticipated pain points include:
1. Withdrawal claims rely on a trusted set of chain attestors.
2. Cross-Chain transactions are slow because they require waiting a challenge period.
3. Cross-Chain transactions are asynchronous, breaking the ability to perform atomic cross-chain transactions (like flash loans).
4. Posting transactions to the Superchain is not-scalable because the transaction data must be submitted to L1 which has limited capacity.
5. There are no easy frameworks for building scalable dApps which utilize many OP Chains.
6. There is no easy wallet for managing assets and dApps across many OP Chains.
If each one of these pain points were addressed, it could be possible to build decentralized alternatives to even the most complex web2 applications.
The following is an overview of potential future enhancements, which when combined, addresses each one of these pain points.
### Multi-Proof Security
#### Pain Point:
1) Withdrawal claims rely on a trusted set of chain attestors.
#### Proposed Solution:
It is possible to replace the trusted set of chain attestors by introducing permissionless proofs—such as Cannon—where dispute resolution is entirely onchain. However, the challenge with entirely onchain proofs is there is no fallback mechanism if they were to break. To ensure that they will never fail, it is possible to introduce a multi-proof system which provides safety through redundancy. For more information on the multi-proof design click [here](https://medium.com/ethereum-optimism/our-pragmatic-path-to-decentralization-cb5805ca43c1).
### Low Latency L2 to L2 Message Passing
#### Pain Point:
2) Cross-Chain transactions are slow because they require waiting a challenge period.
#### Proposed Solution:
Fault proofs introduce a UX burden because they require waiting a challenge period in order to safely finalize. This means that, depending on your challenge period length, users need to wait a long time before their assets are migrated from one OP Chain to the next.
On the other hand, validity proofs do not have this problem. Validity proofs don’t have a challenge period and therefore provide instant withdrawals from one OP Chain to the next. This is extremely important if users are expected to migrate between chains frequently, even during normal dApp execution. However, validity proofs are commonly implemented using zero-knowledge proofs (ZKPs), which are expensive and bug-prone. It will likely take years to truly productionize ZKPs enough such that they can be the primary cross-chain communication protocol.
However, while ZKPs are being productionized, it is possible to achieve low latency L2 to L2 message passing using the OP Stack’s modular proof system. With modular proofs it is possible to use two proof systems for the same chain. This opens up the possibility to provide low latency bridging which trades off security while *also* providing high security high latency bridging.
This heterogeneous bridging system means that developers can build their applications using one of many bridge types, such as:
1. High security, high latency fault proof (standard high security bridge)
2. Low security, low latency fault proof (a short challenge period to achieve low latency)
3. Low security, low latency validity proof (using trusted chain attestors in place of a ZKP)
4. High security, low latency validity proof (once ZKPs are ready)
Mixing multiple proof systems enables developers to provide low latency bridging for low value assets and high latency for high value assets. It is even possible to turn a low security asset which was instantly bridged into a high security asset by proving the asset’s validity using a high security high latency bridge. This building block enables developers to make interesting security tradeoffs such as using a high threshold attestation proof with a high security high latency fault proof fallback.
### Synchronous Cross-Chain Transactions
#### Pain Point:
3) Cross-Chain transactions are asynchronous, breaking the ability to perform atomic cross-chain transactions (like flash loans).
#### Proposed Solution:
Traditional cross-chain messaging is done asynchronously, which means that cross-chain transactions are *not* atomic. For example, if a user would would like to execute a cross-chain arbitrage transaction—buying token A on chain A, and selling token B on chain B—there is no guarantee that their transaction executes in its entirety. The user might end up buying token A without having sold token B.
It is possible to introduce synchronous cross-chain messaging and enable atomic cross-chain interactions by using a shared sequencing protocol on both OP Chains. In our example, the sequencers on chain A and chain B would each receive the arbitrage transaction, come to consensus on when they will include it, and then atomically include each transaction in the linked block. Fees would only be paid if the transaction was indeed included on each chain, meaning the sequencers take the synchronization risk as opposed to the user in our initial example. These shared sequencing protocols can be implemented permissionlessly on top of the modular sequencing layer of the post-Bedrock Superchain.
With the combination of low latency L2 to L2 message passing as well as shared sequencing, it is possible to perform complex transactions such as cross-chain flash loans. It is even possible to go further and create an EVM abstraction where individual smart contracts (or even individual storage slots) exist on different chains.
### Alt-Data Availability Layer — Plasma Protocol
#### Pain Point:
4) Posting transactions to the Superchain is not-scalable because the transaction data must be submitted to L1 which has limited capacity.
#### Proposed Solution:
Today L1 data availability (DA) does not scale nearly enough to be able to support internet-level scale. However, it is possible to extend the amount of data availability accessible to OP Chains by using a Plasma protocol which enables alternative DA providers to supplement the more limited L1 DA.
A generic Plasma protocol is able to scale beyond what is possible on L1 because only the users who are interested in the transaction data will download the Plasma data, whereas on L1 every Ethereum node downloads all of the transaction data on L1. This means that Plasma data is extremely cheap. However, where Plasma has a worse security model than L1—it is possible for Plasma chain data to temporarily become unavailable, meaning users must withdraw from the chain. Note, this security model still guarantees safety of the Plasma chains, just not liveness.
::: details Plasma chain
A chain where transaction data is committed to on L1 but not supplied to L1 directly, with a data availability challenge fallback.
:::
**Plasma protocol overview:**
- Data Availability (DA) Providers receive transaction data from users.
- DA Providers then hash the transaction data and submit the hash to the Plasma Contract.
- Once the hash has been submitted, the DA Provider sends a proof to the user which proves inclusion of their transaction data in the hash. If the DA Provider is misbehaving, they will withhold the proof, not sending it to the user.
- If the DA Provider does not send the proof to the user, the user may submit a DA challenge. This forces the DA Provider to post the transaction data onchain. If the DA Provider does not submit the proof onchain, the hash is deleted. This ensures the user can always (after the challenge period) sync the Plasma chain.
- DA challenge periods may be extended in case of heavy L1 congestion.
- The user may also submit an L1 transaction to withdraw from the Plasma chain in order to switch their DA Provider.
- Settlement of Plasma chains use a near identical fault proof system to Rollup chains with the only difference being that additional data is derived from the chain using the hashes that are finalized in the Plasma Contract.
Because of the ability for hashes to reduce arbitrary size data into a constant size commitment, and the ability to parallelize transaction data hashing, it is possible to achieve near-perfect horizontal scalability of data commitments using Plasma DA. This means that it is possible to put massively scalable applications such as games or social media on Plasma chains.
### Multi-Chain dApp Frameworks
#### Pain Points:
5) There are no easy frameworks for building scalable dApps which utilize many OP Chains.
6) There is no easy wallet for managing assets and dApps across many OP Chains.
#### Proposed Solution (Sketch):
This is not a core protocol change, but instead tooling which can be built on top of the core Superchain protocols. The suggestions here are intended to give rough intuitions for how to build tools which improve the experience of deploying to the Superchain.
These are some tools which could make developing on the Superchain a better experience:
1. Content-addressable smart contracts — this enables contracts to have the same address on all chains. This way developers can write smart contracts which are counterfactually deployed to all OP Chains at the same address. If a user on an OP Chain would like to use the smart contract that is not yet available on their chain, they can independently deploy the code.
2. Cross-chain contract state management standards — creating standards for how smart contract state can migrate from one chain to the next enables developers to shard their applications on many chains. Additionally, this logic can be used in wallets to display user state as if it is all on the same chain. For instance, if a user has tokens split across many chains, the wallet can use the cross-chain state management logic to know that it should display the user balance as a sum of all of their token balances across all chains.
::: tip 📌
For the Ethereum scalability nerds: the state growth problem can be addressed in these frameworks by making it easy to migrate user state from bloated chains into fresh chains. Old bloated chains can be maintained with a low gas limit or deprecated entirely.
:::
3. Superchain RPC endpoint — creating a single RPC endpoint where users can send their Superchain transactions regardless of which OP Chain they are intended to enables users to avoid constantly switching their network.
With robust multi-chain dApp frameworks, it may become as easy to deploy cross-chain dApps as it is to deploy dApps which target a single chain.
## Get Involved
We believe scaling blockchains will radically decentralize the internet and make it easy to create horizontally scalable, secure, and decentralized web applications. We think the Superchain release of the OP Stack could mark a major step towards realizing this vision. However, after the release it will still take an enormous amount of work to realize the scalability vision.
However, with great challenge comes great opportunity! The work needed to arrive at the initial Superchain release of the OP stack, as well as the resulting ecosystem should be exciting greenfields of opportunities for developers who want to contribute. There will be an enormous amount of low hanging fruit contributions unlocked. We can’t pick it alone! The only way we can hope to achieve it is through open source contributions from folks like you! And with [retroactive public goods funding](https://medium.com/ethereum-optimism/retroactive-public-goods-funding-33c9b7d00f0c) your open source contributions may be rewarded too!
Exciting times ahead.
Stay Optimistic 🔴✨
## Glossary
- **Attestation-Based Fault Proof**: A fault proof where challenges can be successfully made by supplying an attestation proof which disagrees with the original withdrawal claim.
- **Attestation-Based Validity Proof**: A validity proof which can be verified by supplying an attestation proof which agrees with the withdrawal claim.
- **Attestation Proof**: A proof which consists of some number of signatures from a pre-agreed upon set of chain attestors.
- **Cannon Fault Proof**: A fault proof where challenges are evaluated using an onchain game which is guaranteed to result in a truthful outcome, given economic rationality assumptions.
- **Chain**: A state [transition system](https://en.wikipedia.org/wiki/Transition_system)—consisting of an initial state, a state transition function, and a list of inputs (transactions)—which is cryptographically committed to and can be independently replicated with commodity computer hardware and internet connection.
- **Chain Proof**: Difficult to forge evidence of the validity of a particular withdrawal claim. Proofs are commonly used to enable chains to communicate with each other.
- **Challenge Period**: The window of time in which a challenge can be made to disprove a fault proof.
- **Fault Proof**: A proof which relies on the absence of counter-evidence to prove correctness.
- **Modular Proof**: The ability to use multiple proof systems for the same OP Chain. For instance, it should be possible to prove an OP Chain using a fault proof or a validity proof.
- **Modular Sequencing**: The ability to configure the sequencer address during OP Chain deployment. This value can be configured by the OP Chain deployer.
- **OP Chain**: An individual chain within the Optimism Superchain. All chains, regardless of their specific properties are considered OP Chains if they are officially governed by the Optimism Collective, and therefore part of the Superchain.
- **Plasma Chain**: A chain where transaction data is committed to on L1 but not supplied to L1 directly, with a data availability challenge fallback.
- **Rollup Chain**: A chain where all transaction data is submitted to L1.
- **Sequencer**: The specific entity or smart contract which has priority when submitting transactions to an OP Chain.
- **Superchain**: A decentralized blockchain platform which consists of many chains that share security and a technology stack (OP Stack). The interoperability and standardization enables individual chains to be treated identically by tools and wallets.
- **Superchain Bridge**: The L1 bridge contracts which govern all OP Chains in the Superchain. This bridge can be upgraded by the Optimism Collective.
- **Validity Proof**: A proof of a withdrawal claim which can be immediately validated, without a challenge period.
- **Withdrawal Claim**: A claim about the state of one chain made on another chain. For instance, I can claim that in OP Mainnet I have burned my tokens with the intent to withdraw those tokens back to L1.
- **Zero Knowledge Proof**: A validity proof which relies on cryptographic properties and low error margins.
---
title: The OP Stack Landscape
lang: en-US
---
**The OP Stack is a common development stack for building L2 blockchain ecosystems, built by the Optimism Collective to power Optimism.**
The OP Stack is best thought of as a collection of software components maintained by the Optimism Collective that either help to define new layers of the stack or fit in as modules within the stack.
Because the OP Stack is a work in progress, the landscape of the different layers and modules is still evolving.
This page sketches out the different conceptual layers of the stack as they exist today and introduces some of the modules that fit into those layers.
This doesn't include all of the modules or layers that may exist in the future, but gives a good overview of the landscape of the OP Stack today.
If you’re interested in learning more about the latest *production* release of the OP Stack, the components of the stack that are highly tested and ready for real-world action, check out the page about the [Bedrock Release](../releases/bedrock.md).
::: warning
Please note that not all of the modules described on this page already exist in a production state — these are explicitly marked as either “**in development**” or “**proposed**
:::
## Existing Landscape
![The OP Stack layers](../../assets/docs/understand/landscape.png)
## Layers
### Data Availability
The Data Availability Layer defines where the raw inputs to an OP Stack based chain are published. An OP Stack chain can use one or more Data Availability module to source its input data. Because an OP Stack chain is derived from the Data Availability Layer, the Data Availability module(s) used have a significant impact on the security model of a system. For example, if a certain piece of data can no longer be retrieved from the Data Availability Layer, it may not be possible to sync the chain.
#### Ethereum DA
Ethereum DA is currently the most widely used Data Availability module for the OP Stack. When using the Ethereum DA module, source data can be derived from any piece of information accessible on the Ethereum blockchain. This includes Ethereum calldata, events, and 4844 data blobs.
- [Specifications](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/specs/derivation.md#batch-submission-wire-format)
- [Source code](https://github.com/ethereum-optimism/optimism/tree/129032f15b76b0d2a940443a39433de931a97a44/op-batcher)
### Sequencing
The Sequencing Layer determines how user transactions on an OP Stack chain are collected and published to the Data Availability Layer module(s) in use. In the default Rollup configuration of the OP Stack, Sequencing is typically handled by a single dedicated Sequencer. Rules defined in the Derivation Layer generally restrict the Sequencer’s ability to withhold transactions for more than a specific period of time. In the proposed future, Sequencing will be modular such that chains can easily select and change the mechanism that controls their current Sequencer.
#### Single Sequencer
The default Sequencer module for the OP Stack is the Single Sequencer module in which a dedicated actor is given the ability to act as the Sequencer. The Single Sequencer module allows a governance mechanism to determine who may act as the Sequencer at any given time.
#### Multiple Sequencer (proposed)
A simple modification to the Single Sequencer module is the Multiple Sequencer module in which the Sequencer at any given time is selected from a pre-defined set of possible actors. Individual OP Stack based chains would be able to determine the exact mechanism that defines the set of possible Sequencers and the mechanism that selects a Sequencer from the set.
### Derivation
The Derivation Layer defines how the raw data in the Data Availability Layer is processed to form the processed inputs that are sent to the Execution Layer via the standard [Ethereum Engine API](https://github.com/ethereum/execution-apis/blob/94164851c1630ff0a9c31d8d7d3d4fb886e196c0/src/engine/README.md). The Derivation Layer may also use the current system state, as defined by the Execution Layer, to inform the parsing of raw input data. The Derivation Layer can be modified to derive Engine API inputs from many different data sources. The Derivation Layer is typically tied closely to the Data Availability Layer because it must understand how to parse any raw input data.
#### Rollup
The Rollup module derives Engine API inputs from Ethereum block data, Sequencer transaction batches, Deposited transaction events, and more.
- [Specifications](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/specs/derivation.md#l2-chain-derivation-pipeline)
- [Source code](https://github.com/ethereum-optimism/optimism/tree/129032f15b76b0d2a940443a39433de931a97a44/op-node)
#### Indexer (proposed)
The Indexer module is a proposed Derivation Layer module that would derive Engine API inputs when transactions are sent to, events are emitted by, or storage is modified in specific smart contracts on a Data Availability Layer module like Ethereum DA.
### Execution
The Execution Layer defines the structure of state within an OP Stack system and defines the state transition function that mutates this state. State transitions are triggered when inputs are received from the Derivation Layer via the Engine API. The Execution Layer abstraction opens up the door to EVM modifications or different underlying VMs entirely.
#### EVM
The EVM is an Execution Layer module that uses the same state representation and state transition function as the Ethereum Virtual Machine. The EVM module in the Ethereum Rollup configuration of the OP Stack is a [lightly modified](https://op-geth.optimism.io/) version of the EVM that adds support for L2 transactions initiated on Ethereum and adds an extra L1 Data Fee to each transaction to account for the cost of publishing transactions to Ethereum.
- [Specifications](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/specs/exec-engine.md) (where it differs from [geth](https://geth.ethereum.org/))
- [Source code](https://github.com/ethereum-optimism/op-geth/tree/09ade3df6d1d3a4f8f308553825348be132bc960)
### Settlement Layer
The Settlement Layer is a mechanism on external blockchains that establish a **view** of the state of an OP Stack chain on those external chains (including other OP Stack chains). For each OP Stack chain, there may be one or more Settlement mechanisms on one or more external chains. Settlement Layer mechanisms are **read-only** and allow parties external to the blockchain to make decisions based on the state of an OP Stack chain.
The term “Settlement Layer” has its origins in the fact that Settlement Layer mechanisms are often used to handle withdrawals of assets out of a blockchain. This sort of withdrawal system first involves proving the state of the target blockchain to some third-party chain and then processing a withdrawal based on that state. However, the Settlement Layer is not strictly (or even predominantly) financial and, at its core, simply allows a third-party chain to become aware of the state of the target chain.
Once a transaction is published and finalized on the corresponding Data Availability layer, the transaction is also finalized on the OP Stack chain. Short of breaking the underlying Data Availability layer, it can no longer be modified or removed. It may not be accepted by the Settlement Layer yet because the Settlement Layer needs to be able to verify transaction *results*, but the transaction itself is already immutable.
#### Attestation-based Fault Proof
An Attestation-based Fault Proof mechanism uses an optimistic protocol to establish a view of an OP Stack chain. In optimistic settlement mechanisms generally, **Proposer** entities can propose what they believe to be the current valid state of the OP Stack chain. If these proposals are not invalidated within a certain period of time (the “challenge period”), then the proposals are assumed by the mechanism to be correct. In the Attestation Proof mechanism in particular, a proposal can be invalidated if some threshold of pre-defined parties provide attestations to a valid state that is different than the state in the proposal. This places a trust assumption on the honesty of at least a threshold number of the pre-defined participants.
- [Specifications](https://github.com/ethereum-optimism/optimism/blob/129032f15b76b0d2a940443a39433de931a97a44/specs/withdrawals.md) (called [withdrawal transactions](https://community.optimism.io/docs/developers/bridge/messaging/#))
- [Source code](https://github.com/ethereum-optimism/optimism/tree/129032f15b76b0d2a940443a39433de931a97a44/packages/contracts-bedrock/contracts)
#### Fault Proof Optimistic Settlement (proposed)
A Fault Proof Optimistic Settlement mechanism is mostly identical to the Attestation-based Fault Proof mechanism used today but it replaces the MultiSig challenger with a permissionless fault proving process. A correctly constructed fault proof should be able to invalidate any incorrect proposals during the allocated challenge period. This places a trust assumption on the correctness of the fault proof construction. At this time, work on the development of a Fault Proof mechanism is well underway.
#### Validity Proof Settlement (proposed)
A Validity Proof Settlement mechanism uses a mathematical proof to attest to the correctness of a proposed view. A proposed state will not be accepted without a valid proof. This places a trust assumption on the correctness of the validity proof construction.
### Governance
The Governance Layer refers to the general set of tools and processes used to manage system configuration, upgrades, and design decisions. This is a relatively abstract layer that can contain a wide range of mechanisms on a target OP Stack chain and on third-party chains that impact many of the other layers of the OP Stack.
#### MultiSig Contracts
MultiSig Contracts are smart contracts that carry out actions when they receive a threshold of signatures from some pre-defined set of participants. These are often used to manage upgrades of components of an OP Stack based system. Currently, this is the mechanism used to manage upgrades of the bridge contracts on Optimism Mainnet. The security of a MultiSig Contract system depends on many different factors, including the number of participants, the threshold, and the safety procedures of each individual participant.
#### Governance Tokens
Governance Tokens are widely used to decentralize decision making. Although the exact functionality of a Governance Token varies on a case-by-case basis, the most common mechanisms allow token holders to vote on some subset of decisions that a project must make. Voting can either be carried out directly or via delegation.
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment