You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@teaclave.apache.org by ms...@apache.org on 2020/05/15 00:34:11 UTC

[incubator-teaclave-website] branch master updated (649d08c -> 943c128)

This is an automated email from the ASF dual-hosted git repository.

mssun pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave-website.git.


    from 649d08c  Start to use vuexpress
     new 1a56b1d  Add more docs
     new 39ccd16  Add Makefile to help build and deploy
     new 715da28  Update navigation bar
     new 943c128  Update Makefile

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .asf.yaml                                         |   6 +
 .vuepress/config.js                               |  31 ++-
 .vuepress/styles/index.styl                       |   3 -
 .vuepress/theme/LICENSE                           |  21 ++
 .vuepress/theme/components/AlgoliaSearchBox.vue   | 170 +++++++++++++++
 .vuepress/theme/components/DropdownLink.vue       | 230 +++++++++++++++++++++
 .vuepress/theme/components/DropdownTransition.vue |  33 +++
 .vuepress/theme/components/Home.vue               | 184 +++++++++++++++++
 .vuepress/theme/components/NavLink.vue            |  87 ++++++++
 .vuepress/theme/components/NavLinks.vue           | 156 ++++++++++++++
 .vuepress/theme/components/Navbar.vue             | 140 +++++++++++++
 .vuepress/theme/components/Page.vue               |  31 +++
 .vuepress/theme/components/PageEdit.vue           | 143 +++++++++++++
 .vuepress/theme/components/PageNav.vue            | 163 +++++++++++++++
 .vuepress/theme/components/Sidebar.vue            |  64 ++++++
 .vuepress/theme/components/SidebarButton.vue      |  40 ++++
 .vuepress/theme/components/SidebarGroup.vue       | 140 +++++++++++++
 .vuepress/theme/components/SidebarLink.vue        | 133 ++++++++++++
 .vuepress/theme/components/SidebarLinks.vue       | 102 +++++++++
 .vuepress/theme/global-components/Badge.vue       |  44 ++++
 .vuepress/theme/index.js                          |  59 ++++++
 .vuepress/theme/layouts/404.vue                   |  30 +++
 .vuepress/theme/layouts/Layout.vue                | 151 ++++++++++++++
 .vuepress/theme/noopModule.js                     |   1 +
 .vuepress/theme/styles/arrow.styl                 |  22 ++
 .vuepress/theme/styles/code.styl                  | 137 ++++++++++++
 .vuepress/theme/styles/config.styl                |   1 +
 .vuepress/theme/styles/custom-blocks.styl         |  44 ++++
 .vuepress/theme/styles/index.styl                 | 201 ++++++++++++++++++
 .vuepress/theme/styles/mobile.styl                |  37 ++++
 .vuepress/theme/styles/toc.styl                   |   3 +
 .vuepress/theme/styles/wrapper.styl               |   9 +
 .vuepress/theme/util/index.js                     | 240 ++++++++++++++++++++++
 Makefile                                          |  12 ++
 index.md                                          |   3 -
 35 files changed, 2863 insertions(+), 8 deletions(-)
 create mode 100644 .asf.yaml
 delete mode 100644 .vuepress/styles/index.styl
 create mode 100644 .vuepress/theme/LICENSE
 create mode 100644 .vuepress/theme/components/AlgoliaSearchBox.vue
 create mode 100644 .vuepress/theme/components/DropdownLink.vue
 create mode 100644 .vuepress/theme/components/DropdownTransition.vue
 create mode 100644 .vuepress/theme/components/Home.vue
 create mode 100644 .vuepress/theme/components/NavLink.vue
 create mode 100644 .vuepress/theme/components/NavLinks.vue
 create mode 100644 .vuepress/theme/components/Navbar.vue
 create mode 100644 .vuepress/theme/components/Page.vue
 create mode 100644 .vuepress/theme/components/PageEdit.vue
 create mode 100644 .vuepress/theme/components/PageNav.vue
 create mode 100644 .vuepress/theme/components/Sidebar.vue
 create mode 100644 .vuepress/theme/components/SidebarButton.vue
 create mode 100644 .vuepress/theme/components/SidebarGroup.vue
 create mode 100644 .vuepress/theme/components/SidebarLink.vue
 create mode 100644 .vuepress/theme/components/SidebarLinks.vue
 create mode 100644 .vuepress/theme/global-components/Badge.vue
 create mode 100644 .vuepress/theme/index.js
 create mode 100644 .vuepress/theme/layouts/404.vue
 create mode 100644 .vuepress/theme/layouts/Layout.vue
 create mode 100644 .vuepress/theme/noopModule.js
 create mode 100644 .vuepress/theme/styles/arrow.styl
 create mode 100644 .vuepress/theme/styles/code.styl
 create mode 100644 .vuepress/theme/styles/config.styl
 create mode 100644 .vuepress/theme/styles/custom-blocks.styl
 create mode 100644 .vuepress/theme/styles/index.styl
 create mode 100644 .vuepress/theme/styles/mobile.styl
 create mode 100644 .vuepress/theme/styles/toc.styl
 create mode 100644 .vuepress/theme/styles/wrapper.styl
 create mode 100644 .vuepress/theme/util/index.js
 create mode 100644 Makefile


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org


[incubator-teaclave-website] 03/04: Update navigation bar

Posted by ms...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mssun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave-website.git

commit 715da28939f3da7ff7ff12f7b7b15bdb351c20ea
Author: Mingshen Sun <bo...@mssun.me>
AuthorDate: Thu May 14 17:31:02 2020 -0700

    Update navigation bar
---
 .vuepress/config.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.vuepress/config.js b/.vuepress/config.js
index 9c4dd7c..53beceb 100644
--- a/.vuepress/config.js
+++ b/.vuepress/config.js
@@ -5,7 +5,7 @@ module.exports = {
     themeConfig: {
         search: false,
         nav: [
-            { text: 'Documentation', link: '/docs/' },
+            { text: 'Documentation', link: '/docs/my-first-function/' },
             { text: 'GitHub', link: 'https://github.com/apache/incubator-teaclave' }
         ],
         sidebar: [


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org


[incubator-teaclave-website] 04/04: Update Makefile

Posted by ms...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mssun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave-website.git

commit 943c12863c88f082320ec6b62ef77e8de49f19e9
Author: Mingshen Sun <bo...@mssun.me>
AuthorDate: Thu May 14 17:33:07 2020 -0700

    Update Makefile
---
 Makefile | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/Makefile b/Makefile
index 2eca33c..511d49d 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,4 @@
-build:
-	git clone https://github.com/apache/incubator-teaclave.git teaclave || cd teaclave && git pull
-	vuepress build -d dist
-	cp .asf.yaml dist
+all: staging site
 
 staging: build
 	ghp-import dist -b asf-staging
@@ -9,4 +6,7 @@ staging: build
 site: build
 	ghp-import dist -b asf-site
 
-all: staging site
+build:
+	git clone https://github.com/apache/incubator-teaclave.git teaclave || cd teaclave && git pull
+	vuepress build -d dist
+	cp .asf.yaml dist


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org


[incubator-teaclave-website] 02/04: Add Makefile to help build and deploy

Posted by ms...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mssun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave-website.git

commit 39ccd16f65a114201b2eb3512220088bbf21f45c
Author: Mingshen Sun <bo...@mssun.me>
AuthorDate: Thu May 14 17:22:47 2020 -0700

    Add Makefile to help build and deploy
---
 .asf.yaml |  6 ++++++
 Makefile  | 11 ++++++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/.asf.yaml b/.asf.yaml
new file mode 100644
index 0000000..d046a82
--- /dev/null
+++ b/.asf.yaml
@@ -0,0 +1,6 @@
+staging:
+  profile: ~
+  whoami:  asf-staging
+
+publish:
+  whoami:  asf-site
diff --git a/Makefile b/Makefile
index 13a01d6..2eca33c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,12 @@
-all:
+build:
 	git clone https://github.com/apache/incubator-teaclave.git teaclave || cd teaclave && git pull
 	vuepress build -d dist
+	cp .asf.yaml dist
+
+staging: build
+	ghp-import dist -b asf-staging
+
+site: build
+	ghp-import dist -b asf-site
+
+all: staging site


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org


[incubator-teaclave-website] 01/04: Add more docs

Posted by ms...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mssun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-teaclave-website.git

commit 1a56b1d56ea1174db7cfe8823a52ee1aa8a20f46
Author: Mingshen Sun <bo...@mssun.me>
AuthorDate: Thu May 14 17:02:55 2020 -0700

    Add more docs
---
 .vuepress/config.js                               |  31 ++-
 .vuepress/styles/index.styl                       |   3 -
 .vuepress/theme/LICENSE                           |  21 ++
 .vuepress/theme/components/AlgoliaSearchBox.vue   | 170 +++++++++++++++
 .vuepress/theme/components/DropdownLink.vue       | 230 +++++++++++++++++++++
 .vuepress/theme/components/DropdownTransition.vue |  33 +++
 .vuepress/theme/components/Home.vue               | 184 +++++++++++++++++
 .vuepress/theme/components/NavLink.vue            |  87 ++++++++
 .vuepress/theme/components/NavLinks.vue           | 156 ++++++++++++++
 .vuepress/theme/components/Navbar.vue             | 140 +++++++++++++
 .vuepress/theme/components/Page.vue               |  31 +++
 .vuepress/theme/components/PageEdit.vue           | 143 +++++++++++++
 .vuepress/theme/components/PageNav.vue            | 163 +++++++++++++++
 .vuepress/theme/components/Sidebar.vue            |  64 ++++++
 .vuepress/theme/components/SidebarButton.vue      |  40 ++++
 .vuepress/theme/components/SidebarGroup.vue       | 140 +++++++++++++
 .vuepress/theme/components/SidebarLink.vue        | 133 ++++++++++++
 .vuepress/theme/components/SidebarLinks.vue       | 102 +++++++++
 .vuepress/theme/global-components/Badge.vue       |  44 ++++
 .vuepress/theme/index.js                          |  59 ++++++
 .vuepress/theme/layouts/404.vue                   |  30 +++
 .vuepress/theme/layouts/Layout.vue                | 151 ++++++++++++++
 .vuepress/theme/noopModule.js                     |   1 +
 .vuepress/theme/styles/arrow.styl                 |  22 ++
 .vuepress/theme/styles/code.styl                  | 137 ++++++++++++
 .vuepress/theme/styles/config.styl                |   1 +
 .vuepress/theme/styles/custom-blocks.styl         |  44 ++++
 .vuepress/theme/styles/index.styl                 | 201 ++++++++++++++++++
 .vuepress/theme/styles/mobile.styl                |  37 ++++
 .vuepress/theme/styles/toc.styl                   |   3 +
 .vuepress/theme/styles/wrapper.styl               |   9 +
 .vuepress/theme/util/index.js                     | 240 ++++++++++++++++++++++
 Makefile                                          |   3 +
 index.md                                          |   3 -
 34 files changed, 2848 insertions(+), 8 deletions(-)

diff --git a/.vuepress/config.js b/.vuepress/config.js
index 0a48a78..9c4dd7c 100644
--- a/.vuepress/config.js
+++ b/.vuepress/config.js
@@ -1,10 +1,37 @@
 module.exports = {
-    title: 'Apache Teaclave',
-    description: 'Teaclave is an open source universal secure computing platform, making computation on privacy-sensitive data safe and simple.',
+    title: 'Apache Teaclave (incubating)',
+    description: 'Apache Teaclave (incubating) is an open source universal secure computing platform, making computation on privacy-sensitive data safe and simple.',
+    base: '/',
     themeConfig: {
         search: false,
         nav: [
+            { text: 'Documentation', link: '/docs/' },
             { text: 'GitHub', link: 'https://github.com/apache/incubator-teaclave' }
+        ],
+        sidebar: [
+            {
+                title: 'Documentation',
+                path: '/docs/my-first-function/',
+                collapsable: false,
+                children: [
+                    '/teaclave/docs/my-first-function',
+                    '/teaclave/docs/threat-model',
+                    '/teaclave/docs/rust-guideline',
+                    '/teaclave/docs/mutual-attestation',
+                ],
+            },
+            {
+                title: 'Codebase',
+                path: '/services/',
+                collapsable: false,
+                children: [
+                    '/teaclave/services/',
+                    '/teaclave/config/',
+                    '/teaclave/dcap/',
+                    '/teaclave/keys/',
+                    '/teaclave/docker/',
+                ],
+            },
         ]
     },
 }
diff --git a/.vuepress/styles/index.styl b/.vuepress/styles/index.styl
deleted file mode 100644
index e1ab8a3..0000000
--- a/.vuepress/styles/index.styl
+++ /dev/null
@@ -1,3 +0,0 @@
-.footer {
-  font-size: 12px;
-}
diff --git a/.vuepress/theme/LICENSE b/.vuepress/theme/LICENSE
new file mode 100644
index 0000000..15f1f7e
--- /dev/null
+++ b/.vuepress/theme/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2018-present, Yuxi (Evan) You
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/.vuepress/theme/components/AlgoliaSearchBox.vue b/.vuepress/theme/components/AlgoliaSearchBox.vue
new file mode 100644
index 0000000..4d51a71
--- /dev/null
+++ b/.vuepress/theme/components/AlgoliaSearchBox.vue
@@ -0,0 +1,170 @@
+<template>
+  <form
+    id="search-form"
+    class="algolia-search-wrapper search-box"
+    role="search"
+  >
+    <input
+      id="algolia-search-input"
+      class="search-query"
+      :placeholder="placeholder"
+    >
+  </form>
+</template>
+
+<script>
+export default {
+  name: 'AlgoliaSearchBox',
+
+  props: ['options'],
+
+  data () {
+    return {
+      placeholder: undefined
+    }
+  },
+
+  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) {
+      Promise.all([
+        import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'),
+        import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css')
+      ]).then(([docsearch]) => {
+        docsearch = docsearch.default
+        const { algoliaOptions = {}} = userOptions
+        docsearch(Object.assign(
+          {},
+          userOptions,
+          {
+            inputSelector: '#algolia-search-input',
+            // #697 Make docsearch work well at i18n mode.
+            algoliaOptions: Object.assign({
+              'facetFilters': [`lang:${lang}`].concat(algoliaOptions.facetFilters || [])
+            }, algoliaOptions),
+            handleSelected: (input, event, suggestion) => {
+              const { pathname, hash } = new URL(suggestion.url)
+              const routepath = pathname.replace(this.$site.base, '/')
+              this.$router.push(`${routepath}${hash}`)
+            }
+          }
+        ))
+      })
+    },
+
+    update (options, lang) {
+      this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">'
+      this.initialize(options, lang)
+    }
+  }
+}
+</script>
+
+<style lang="stylus">
+.algolia-search-wrapper
+  & > span
+    vertical-align middle
+  .algolia-autocomplete
+    line-height normal
+    .ds-dropdown-menu
+      background-color #fff
+      border 1px solid #999
+      border-radius 4px
+      font-size 16px
+      margin 6px 0 0
+      padding 4px
+      text-align left
+      &:before
+        border-color #999
+      [class*=ds-dataset-]
+        border none
+        padding 0
+      .ds-suggestions
+        margin-top 0
+      .ds-suggestion
+        border-bottom 1px solid $borderColor
+    .algolia-docsearch-suggestion--highlight
+      color #2c815b
+    .algolia-docsearch-suggestion
+      border-color $borderColor
+      padding 0
+      .algolia-docsearch-suggestion--category-header
+        padding 5px 10px
+        margin-top 0
+        background $accentColor
+        color #fff
+        font-weight 600
+        .algolia-docsearch-suggestion--highlight
+          background rgba(255, 255, 255, 0.6)
+      .algolia-docsearch-suggestion--wrapper
+        padding 0
+      .algolia-docsearch-suggestion--title
+        font-weight 600
+        margin-bottom 0
+        color $textColor
+      .algolia-docsearch-suggestion--subcategory-column
+        vertical-align top
+        padding 5px 7px 5px 5px
+        border-color $borderColor
+        background #f1f3f5
+        &:after
+          display none
+      .algolia-docsearch-suggestion--subcategory-column-text
+        color #555
+    .algolia-docsearch-footer
+      border-color $borderColor
+    .ds-cursor .algolia-docsearch-suggestion--content
+      background-color #e7edf3 !important
+      color $textColor
+
+@media (min-width: $MQMobile)
+  .algolia-search-wrapper
+    .algolia-autocomplete
+      .algolia-docsearch-suggestion
+        .algolia-docsearch-suggestion--subcategory-column
+          float none
+          width 150px
+          min-width 150px
+          display table-cell
+        .algolia-docsearch-suggestion--content
+          float none
+          display table-cell
+          width 100%
+          vertical-align top
+        .ds-dropdown-menu
+          min-width 515px !important
+
+@media (max-width: $MQMobile)
+  .algolia-search-wrapper
+    .ds-dropdown-menu
+      min-width calc(100vw - 4rem) !important
+      max-width calc(100vw - 4rem) !important
+    .algolia-docsearch-suggestion--wrapper
+      padding 5px 7px 5px 5px !important
+    .algolia-docsearch-suggestion--subcategory-column
+      padding 0 !important
+      background white !important
+    .algolia-docsearch-suggestion--subcategory-column-text:after
+      content " > "
+      font-size 10px
+      line-height 14.4px
+      display inline-block
+      width 5px
+      margin -3px 3px 0
+      vertical-align middle
+
+</style>
diff --git a/.vuepress/theme/components/DropdownLink.vue b/.vuepress/theme/components/DropdownLink.vue
new file mode 100644
index 0000000..0ca7137
--- /dev/null
+++ b/.vuepress/theme/components/DropdownLink.vue
@@ -0,0 +1,230 @@
+<template>
+  <div
+    class="dropdown-wrapper"
+    :class="{ open }"
+  >
+    <button
+      class="dropdown-title"
+      type="button"
+      :aria-label="dropdownAriaLabel"
+      @click="setOpen(!open)"
+    >
+      <span class="title">{{ item.text }}</span>
+      <span
+        class="arrow"
+        :class="open ? 'down' : 'right'"
+      />
+    </button>
+
+    <DropdownTransition>
+      <ul
+        v-show="open"
+        class="nav-dropdown"
+      >
+        <li
+          v-for="(subItem, index) in item.items"
+          :key="subItem.link || index"
+          class="dropdown-item"
+        >
+          <h4 v-if="subItem.type === 'links'">
+            {{ subItem.text }}
+          </h4>
+
+          <ul
+            v-if="subItem.type === 'links'"
+            class="dropdown-subitem-wrapper"
+          >
+            <li
+              v-for="childSubItem in subItem.items"
+              :key="childSubItem.link"
+              class="dropdown-subitem"
+            >
+              <NavLink
+                :item="childSubItem"
+                @focusout="
+                  isLastItemOfArray(childSubItem, subItem.items) &&
+                    isLastItemOfArray(subItem, item.items) &&
+                    setOpen(false)
+                "
+              />
+            </li>
+          </ul>
+
+          <NavLink
+            v-else
+            :item="subItem"
+            @focusout="isLastItemOfArray(subItem, item.items) && setOpen(false)"
+          />
+        </li>
+      </ul>
+    </DropdownTransition>
+  </div>
+</template>
+
+<script>
+import NavLink from '@theme/components/NavLink.vue'
+import DropdownTransition from '@theme/components/DropdownTransition.vue'
+import last from 'lodash/last'
+
+export default {
+  name: 'DropdownLink',
+
+  components: {
+    NavLink,
+    DropdownTransition
+  },
+
+  props: {
+    item: {
+      required: true
+    }
+  },
+
+  data () {
+    return {
+      open: false
+    }
+  },
+
+  computed: {
+    dropdownAriaLabel () {
+      return this.item.ariaLabel || this.item.text
+    }
+  },
+
+  watch: {
+    $route () {
+      this.open = false
+    }
+  },
+
+  methods: {
+    setOpen (value) {
+      this.open = value
+    },
+
+    isLastItemOfArray (item, array) {
+      return last(array) === item
+    }
+  }
+}
+</script>
+
+<style lang="stylus">
+.dropdown-wrapper
+  cursor pointer
+  .dropdown-title
+    display block
+    font-size 0.9rem
+    font-family inherit
+    cursor inherit
+    padding inherit
+    line-height 1.4rem
+    background transparent
+    border none
+    font-weight 500
+    color $textColor
+    &:hover
+      border-color transparent
+    .arrow
+      vertical-align middle
+      margin-top -1px
+      margin-left 0.4rem
+  .nav-dropdown
+    .dropdown-item
+      color inherit
+      line-height 1.7rem
+      h4
+        margin 0.45rem 0 0
+        border-top 1px solid #eee
+        padding 0.45rem 1.5rem 0 1.25rem
+      .dropdown-subitem-wrapper
+        padding 0
+        list-style none
+        .dropdown-subitem
+          font-size 0.9em
+      a
+        display block
+        line-height 1.7rem
+        position relative
+        border-bottom none
+        font-weight 400
+        margin-bottom 0
+        padding 0 1.5rem 0 1.25rem
+        &:hover
+          color $accentColor
+        &.router-link-active
+          color $accentColor
+          &::after
+            content ""
+            width 0
+            height 0
+            border-left 5px solid $accentColor
+            border-top 3px solid transparent
+            border-bottom 3px solid transparent
+            position absolute
+            top calc(50% - 2px)
+            left 9px
+      &:first-child h4
+        margin-top 0
+        padding-top 0
+        border-top 0
+
+@media (max-width: $MQMobile)
+  .dropdown-wrapper
+    &.open .dropdown-title
+      margin-bottom 0.5rem
+    .dropdown-title
+      font-weight 600
+      font-size inherit
+      &:hover
+        color $accentColor
+    .nav-dropdown
+      transition height .1s ease-out
+      overflow hidden
+      .dropdown-item
+        h4
+          border-top 0
+          margin-top 0
+          padding-top 0
+        h4, & > a
+          font-size 15px
+          line-height 2rem
+        .dropdown-subitem
+          font-size 14px
+          padding-left 1rem
+
+@media (min-width: $MQMobile)
+  .dropdown-wrapper
+    height 1.8rem
+    &:hover .nav-dropdown,
+    &.open .nav-dropdown
+      // override the inline style.
+      display block !important
+    &.open:blur
+      display none
+    .dropdown-title .arrow
+      // make the arrow always down at desktop
+      border-left 4px solid transparent
+      border-right 4px solid transparent
+      border-top 6px solid $arrowBgColor
+      border-bottom 0
+    .nav-dropdown
+      display none
+      // Avoid height shaked by clicking
+      height auto !important
+      box-sizing border-box;
+      max-height calc(100vh - 2.7rem)
+      overflow-y auto
+      position absolute
+      top 100%
+      right 0
+      background-color #fff
+      padding 0.6rem 0
+      border 1px solid #ddd
+      border-bottom-color #ccc
+      text-align left
+      border-radius 0.25rem
+      white-space nowrap
+      margin 0
+</style>
diff --git a/.vuepress/theme/components/DropdownTransition.vue b/.vuepress/theme/components/DropdownTransition.vue
new file mode 100644
index 0000000..eeaf12b
--- /dev/null
+++ b/.vuepress/theme/components/DropdownTransition.vue
@@ -0,0 +1,33 @@
+<template>
+  <transition
+    name="dropdown"
+    @enter="setHeight"
+    @after-enter="unsetHeight"
+    @before-leave="setHeight"
+  >
+    <slot />
+  </transition>
+</template>
+
+<script>
+export default {
+  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 = ''
+    }
+  }
+}
+</script>
+
+<style lang="stylus">
+.dropdown-enter, .dropdown-leave-to
+  height 0 !important
+
+</style>
diff --git a/.vuepress/theme/components/Home.vue b/.vuepress/theme/components/Home.vue
new file mode 100644
index 0000000..c11cba7
--- /dev/null
+++ b/.vuepress/theme/components/Home.vue
@@ -0,0 +1,184 @@
+<template>
+  <main
+    class="home"
+    aria-labelledby="main-title"
+  >
+    <header class="hero">
+      <img
+        v-if="data.heroImage"
+        :src="$withBase(data.heroImage)"
+        :alt="data.heroAlt || 'hero'"
+      >
+
+      <h1
+        v-if="data.heroText !== null"
+        id="main-title"
+      >
+        {{ data.heroText || $title || 'Hello' }}
+      </h1>
+
+      <p
+        v-if="data.tagline !== null"
+        class="description"
+      >
+        {{ data.tagline || $description || 'Welcome to your VuePress site' }}
+      </p>
+
+      <p
+        v-if="data.actionText && data.actionLink"
+        class="action"
+      >
+        <NavLink
+          class="action-button"
+          :item="actionLink"
+        />
+      </p>
+    </header>
+
+    <div
+      v-if="data.features && data.features.length"
+      class="features"
+    >
+      <div
+        v-for="(feature, index) in data.features"
+        :key="index"
+        class="feature"
+      >
+        <h2>{{ feature.title }}</h2>
+        <p>{{ feature.details }}</p>
+      </div>
+    </div>
+
+    <Content class="theme-default-content custom" />
+
+    <div class="footer">
+     Apache Teaclave is an effort undergoing incubation at The Apache Software
+     Foundation (ASF), sponsored by the Apache Incubator. Incubation is required
+     of all newly accepted projects until a further review indicates that the
+     infrastructure, communications, and decision making process have stabilized
+     in a manner consistent with other successful ASF projects. While incubation
+     status is not necessarily a reflection of the completeness or stability of
+     the code, it does indicate that the project has yet to be fully endorsed by
+     the ASF. Copyright © 2020 The Apache Software Foundation. Apache Teaclave,
+     Apache, the Apache feather, and the Apache Teaclave project logo are either
+     trademarks or registered trademarks of the Apache Software Foundation. See
+     also other useful ASF links: Apache Homepage, License Sponsorship, Security
+     Thanks, Current Event
+    </div>
+  </main>
+</template>
+
+<script>
+import NavLink from '@theme/components/NavLink.vue'
+
+export default {
+  name: 'Home',
+
+  components: { NavLink },
+
+  computed: {
+    data () {
+      return this.$page.frontmatter
+    },
+
+    actionLink () {
+      return {
+        link: this.data.actionLink,
+        text: this.data.actionText
+      }
+    }
+  }
+}
+</script>
+
+<style lang="stylus">
+.home
+  padding $navbarHeight 2rem 0
+  max-width $homePageWidth
+  margin 0px auto
+  display block
+  .hero
+    text-align center
+    img
+      max-width: 100%
+      max-height 280px
+      display block
+      margin 3rem auto 1.5rem
+    h1
+      font-size 3rem
+    h1, .description, .action
+      margin 1.8rem auto
+    .description
+      max-width 45rem
+      font-size 1.6rem
+      line-height 1.3
+      color lighten($textColor, 40%)
+    .action-button
+      display inline-block
+      font-size 1.2rem
+      color #fff
+      background-color $accentColor
+      padding 0.8rem 1.6rem
+      border-radius 4px
+      transition background-color .1s ease
+      box-sizing border-box
+      border-bottom 1px solid darken($accentColor, 10%)
+      &:hover
+        background-color lighten($accentColor, 10%)
+  .features
+    border-top 1px solid $borderColor
+    padding 1.2rem 0
+    margin-top 2.5rem
+    display flex
+    flex-wrap wrap
+    align-items flex-start
+    align-content stretch
+    justify-content space-between
+  .feature
+    flex-grow 1
+    flex-basis 30%
+    max-width 30%
+    h2
+      font-size 1.4rem
+      font-weight 500
+      border-bottom none
+      padding-bottom 0
+      color lighten($textColor, 10%)
+    p
+      color lighten($textColor, 25%)
+  .footer
+    font-size 0.7rem
+    padding 2.5rem
+    border-top 1px solid $borderColor
+    text-align center
+    color lighten($textColor, 25%)
+
+@media (max-width: $MQMobile)
+  .home
+    .features
+      flex-direction column
+    .feature
+      max-width 100%
+      padding 0 2.5rem
+
+@media (max-width: $MQMobileNarrow)
+  .home
+    padding-left 1.5rem
+    padding-right 1.5rem
+    .hero
+      img
+        max-height 210px
+        margin 2rem auto 1.2rem
+      h1
+        font-size 2rem
+      h1, .description, .action
+        margin 1.2rem auto
+      .description
+        font-size 1.2rem
+      .action-button
+        font-size 1rem
+        padding 0.6rem 1.2rem
+    .feature
+      h2
+        font-size 1.25rem
+</style>
diff --git a/.vuepress/theme/components/NavLink.vue b/.vuepress/theme/components/NavLink.vue
new file mode 100644
index 0000000..4ccd13d
--- /dev/null
+++ b/.vuepress/theme/components/NavLink.vue
@@ -0,0 +1,87 @@
+<template>
+  <RouterLink
+    v-if="isInternal"
+    class="nav-link"
+    :to="link"
+    :exact="exact"
+    @focusout.native="focusoutAction"
+  >
+    {{ item.text }}
+  </RouterLink>
+  <a
+    v-else
+    :href="link"
+    class="nav-link external"
+    :target="target"
+    :rel="rel"
+    @focusout="focusoutAction"
+  >
+    {{ item.text }}
+    <OutboundLink v-if="isBlankTarget" />
+  </a>
+</template>
+
+<script>
+import { isExternal, isMailto, isTel, ensureExt } from '../util'
+
+export default {
+  name: 'NavLink',
+
+  props: {
+    item: {
+      required: true
+    }
+  },
+
+  computed: {
+    link () {
+      return ensureExt(this.item.link)
+    },
+
+    exact () {
+      if (this.$site.locales) {
+        return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link)
+      }
+      return 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) {
+        return this.item.rel
+      }
+      return this.isBlankTarget ? 'noopener noreferrer' : ''
+    }
+  },
+
+  methods: {
+    focusoutAction () {
+      this.$emit('focusout')
+    }
+  }
+}
+</script>
diff --git a/.vuepress/theme/components/NavLinks.vue b/.vuepress/theme/components/NavLinks.vue
new file mode 100644
index 0000000..2656ae2
--- /dev/null
+++ b/.vuepress/theme/components/NavLinks.vue
@@ -0,0 +1,156 @@
+<template>
+  <nav
+    v-if="userLinks.length || repoLink"
+    class="nav-links"
+  >
+    <!-- user links -->
+    <div
+      v-for="item in userLinks"
+      :key="item.link"
+      class="nav-item"
+    >
+      <DropdownLink
+        v-if="item.type === 'links'"
+        :item="item"
+      />
+      <NavLink
+        v-else
+        :item="item"
+      />
+    </div>
+
+    <!-- repo link -->
+    <a
+      v-if="repoLink"
+      :href="repoLink"
+      class="repo-link"
+      target="_blank"
+      rel="noopener noreferrer"
+    >
+      {{ repoLabel }}
+      <OutboundLink />
+    </a>
+  </nav>
+</template>
+
+<script>
+import DropdownLink from '@theme/components/DropdownLink.vue'
+import { resolveNavLinkItem } from '../util'
+import NavLink from '@theme/components/NavLink.vue'
+
+export default {
+  name: 'NavLinks',
+
+  components: {
+    NavLink,
+    DropdownLink
+  },
+
+  computed: {
+    userNav () {
+      return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || []
+    },
+
+    nav () {
+      const { locales } = this.$site
+      if (locales && Object.keys(locales).length > 1) {
+        const currentLink = this.$page.path
+        const routes = this.$router.options.routes
+        const themeLocales = this.$site.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
+            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 [...this.userNav, languageDropdown]
+      }
+      return this.userNav
+    },
+
+    userLinks () {
+      return (this.nav || []).map(link => {
+        return Object.assign(resolveNavLinkItem(link), {
+          items: (link.items || []).map(resolveNavLinkItem)
+        })
+      })
+    },
+
+    repoLink () {
+      const { repo } = this.$site.themeConfig
+      if (repo) {
+        return /^https?:/.test(repo)
+          ? repo
+          : `https://github.com/${repo}`
+      }
+      return null
+    },
+
+    repoLabel () {
+      if (!this.repoLink) return
+      if (this.$site.themeConfig.repoLabel) {
+        return this.$site.themeConfig.repoLabel
+      }
+
+      const repoHost = this.repoLink.match(/^https?:\/\/[^/]+/)[0]
+      const platforms = ['GitHub', 'GitLab', 'Bitbucket']
+      for (let i = 0; i < platforms.length; i++) {
+        const platform = platforms[i]
+        if (new RegExp(platform, 'i').test(repoHost)) {
+          return platform
+        }
+      }
+
+      return 'Source'
+    }
+  }
+}
+</script>
+
+<style lang="stylus">
+.nav-links
+  display inline-block
+  a
+    line-height 1.4rem
+    color inherit
+    &:hover, &.router-link-active
+      color $accentColor
+  .nav-item
+    position relative
+    display inline-block
+    margin-left 1.5rem
+    line-height 2rem
+    &:first-child
+      margin-left 0
+  .repo-link
+    margin-left 1.5rem
+
+@media (max-width: $MQMobile)
+  .nav-links
+    .nav-item, .repo-link
+      margin-left 0
+
+@media (min-width: $MQMobile)
+  .nav-links a
+    &:hover, &.router-link-active
+      color $textColor
+  .nav-item > a:not(.external)
+    &:hover, &.router-link-active
+      margin-bottom -2px
+      border-bottom 2px solid lighten($accentColor, 8%)
+</style>
diff --git a/.vuepress/theme/components/Navbar.vue b/.vuepress/theme/components/Navbar.vue
new file mode 100644
index 0000000..f8dd49c
--- /dev/null
+++ b/.vuepress/theme/components/Navbar.vue
@@ -0,0 +1,140 @@
+<template>
+  <header class="navbar">
+    <SidebarButton @toggle-sidebar="$emit('toggle-sidebar')" />
+
+    <RouterLink
+      :to="$localePath"
+      class="home-link"
+    >
+      <img
+        v-if="$site.themeConfig.logo"
+        class="logo"
+        :src="$withBase($site.themeConfig.logo)"
+        :alt="$siteTitle"
+      >
+      <span
+        v-if="$siteTitle"
+        ref="siteName"
+        class="site-name"
+        :class="{ 'can-hide': $site.themeConfig.logo }"
+      >{{ $siteTitle }}</span>
+    </RouterLink>
+
+    <div
+      class="links"
+      :style="linksWrapMaxWidth ? {
+        'max-width': linksWrapMaxWidth + 'px'
+      } : {}"
+    >
+      <AlgoliaSearchBox
+        v-if="isAlgoliaSearch"
+        :options="algolia"
+      />
+      <SearchBox v-else-if="$site.themeConfig.search !== false && $page.frontmatter.search !== false" />
+      <NavLinks class="can-hide" />
+    </div>
+  </header>
+</template>
+
+<script>
+import AlgoliaSearchBox from '@AlgoliaSearchBox'
+import SearchBox from '@SearchBox'
+import SidebarButton from '@theme/components/SidebarButton.vue'
+import NavLinks from '@theme/components/NavLinks.vue'
+
+export default {
+  name: 'Navbar',
+
+  components: {
+    SidebarButton,
+    NavLinks,
+    SearchBox,
+    AlgoliaSearchBox
+  },
+
+  data () {
+    return {
+      linksWrapMaxWidth: null
+    }
+  },
+
+  computed: {
+    algolia () {
+      return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
+    },
+
+    isAlgoliaSearch () {
+      return this.algolia && this.algolia.apiKey && this.algolia.indexName
+    }
+  },
+
+  mounted () {
+    const MOBILE_DESKTOP_BREAKPOINT = 719 // refer to config.styl
+    const NAVBAR_VERTICAL_PADDING = parseInt(css(this.$el, 'paddingLeft')) + parseInt(css(this.$el, 'paddingRight'))
+    const handleLinksWrapWidth = () => {
+      if (document.documentElement.clientWidth < MOBILE_DESKTOP_BREAKPOINT) {
+        this.linksWrapMaxWidth = null
+      } else {
+        this.linksWrapMaxWidth = this.$el.offsetWidth - NAVBAR_VERTICAL_PADDING
+          - (this.$refs.siteName && this.$refs.siteName.offsetWidth || 0)
+      }
+    }
+    handleLinksWrapWidth()
+    window.addEventListener('resize', handleLinksWrapWidth, false)
+  }
+}
+
+function css (el, property) {
+  // NOTE: Known bug, will return 'auto' if style value is 'auto'
+  const win = el.ownerDocument.defaultView
+  // null means not to return pseudo styles
+  return win.getComputedStyle(el, null)[property]
+}
+</script>
+
+<style lang="stylus">
+$navbar-vertical-padding = 0.7rem
+$navbar-horizontal-padding = 1.5rem
+
+.navbar
+  padding $navbar-vertical-padding $navbar-horizontal-padding
+  line-height $navbarHeight - 1.4rem
+  a, span, img
+    display inline-block
+  .logo
+    height $navbarHeight - 1.4rem
+    min-width $navbarHeight - 1.4rem
+    margin-right 0.8rem
+    vertical-align top
+  .site-name
+    font-size 1.3rem
+    font-weight 600
+    color $textColor
+    position relative
+  .links
+    padding-left 1.5rem
+    box-sizing border-box
+    background-color white
+    white-space nowrap
+    font-size 0.9rem
+    position absolute
+    right $navbar-horizontal-padding
+    top $navbar-vertical-padding
+    display flex
+    .search-box
+      flex: 0 0 auto
+      vertical-align top
+
+@media (max-width: $MQMobile)
+  .navbar
+    padding-left 4rem
+    .can-hide
+      display none
+    .links
+      padding-left 1.5rem
+    .site-name
+      width calc(100vw - 9.4rem)
+      overflow hidden
+      white-space nowrap
+      text-overflow ellipsis
+</style>
diff --git a/.vuepress/theme/components/Page.vue b/.vuepress/theme/components/Page.vue
new file mode 100644
index 0000000..04ec7cb
--- /dev/null
+++ b/.vuepress/theme/components/Page.vue
@@ -0,0 +1,31 @@
+<template>
+  <main class="page">
+    <slot name="top" />
+
+    <Content class="theme-default-content" />
+    <PageEdit />
+
+    <PageNav v-bind="{ sidebarItems }" />
+
+    <slot name="bottom" />
+  </main>
+</template>
+
+<script>
+import PageEdit from '@theme/components/PageEdit.vue'
+import PageNav from '@theme/components/PageNav.vue'
+
+export default {
+  components: { PageEdit, PageNav },
+  props: ['sidebarItems']
+}
+</script>
+
+<style lang="stylus">
+@require '../styles/wrapper.styl'
+
+.page
+  padding-bottom 2rem
+  display block
+
+</style>
diff --git a/.vuepress/theme/components/PageEdit.vue b/.vuepress/theme/components/PageEdit.vue
new file mode 100644
index 0000000..e1ac3ca
--- /dev/null
+++ b/.vuepress/theme/components/PageEdit.vue
@@ -0,0 +1,143 @@
+<template>
+  <footer class="page-edit">
+    <div
+      v-if="editLink"
+      class="edit-link"
+    >
+      <a
+        :href="editLink"
+        target="_blank"
+        rel="noopener noreferrer"
+      >{{ editLinkText }}</a>
+      <OutboundLink />
+    </div>
+
+    <div
+      v-if="lastUpdated"
+      class="last-updated"
+    >
+      <span class="prefix">{{ lastUpdatedText }}:</span>
+      <span class="time">{{ lastUpdated }}</span>
+    </div>
+  </footer>
+</template>
+
+<script>
+import isNil from 'lodash/isNil'
+import { endingSlashRE, outboundRE } from '../util'
+
+export default {
+  name: 'PageEdit',
+
+  computed: {
+    lastUpdated () {
+      return this.$page.lastUpdated
+    },
+
+    lastUpdatedText () {
+      if (typeof this.$themeLocaleConfig.lastUpdated === 'string') {
+        return this.$themeLocaleConfig.lastUpdated
+      }
+      if (typeof this.$site.themeConfig.lastUpdated === 'string') {
+        return this.$site.themeConfig.lastUpdated
+      }
+      return 'Last Updated'
+    },
+
+    editLink () {
+      const showEditLink = isNil(this.$page.frontmatter.editLink)
+        ? this.$site.themeConfig.editLinks
+        : this.$page.frontmatter.editLink
+
+      const {
+        repo,
+        docsDir = '',
+        docsBranch = 'master',
+        docsRepo = repo
+      } = this.$site.themeConfig
+
+      if (showEditLink && docsRepo && this.$page.relativePath) {
+        return this.createEditLink(
+          repo,
+          docsRepo,
+          docsDir,
+          docsBranch,
+          this.$page.relativePath
+        )
+      }
+      return null
+    },
+
+    editLinkText () {
+      return (
+        this.$themeLocaleConfig.editLinkText
+        || this.$site.themeConfig.editLinkText
+        || `Edit this page`
+      )
+    }
+  },
+
+  methods: {
+    createEditLink (repo, docsRepo, docsDir, docsBranch, path) {
+      const bitbucket = /bitbucket.org/
+      if (bitbucket.test(repo)) {
+        const base = outboundRE.test(docsRepo) ? docsRepo : repo
+        return (
+          base.replace(endingSlashRE, '')
+          + `/src`
+          + `/${docsBranch}/`
+          + (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '')
+          + path
+          + `?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
+        )
+      }
+
+      const base = outboundRE.test(docsRepo)
+        ? docsRepo
+        : `https://github.com/${docsRepo}`
+      return (
+        base.replace(endingSlashRE, '')
+        + `/edit`
+        + `/${docsBranch}/`
+        + (docsDir ? docsDir.replace(endingSlashRE, '') + '/' : '')
+        + path
+      )
+    }
+  }
+}
+</script>
+
+<style lang="stylus">
+@require '../styles/wrapper.styl'
+
+.page-edit
+  @extend $wrapper
+  padding-top 1rem
+  padding-bottom 1rem
+  overflow auto
+
+  .edit-link
+    display inline-block
+    a
+      color lighten($textColor, 25%)
+      margin-right 0.25rem
+  .last-updated
+    float right
+    font-size 0.9em
+    .prefix
+      font-weight 500
+      color lighten($textColor, 25%)
+    .time
+      font-weight 400
+      color #aaa
+
+@media (max-width: $MQMobile)
+  .page-edit
+    .edit-link
+      margin-bottom 0.5rem
+    .last-updated
+      font-size 0.8em
+      float none
+      text-align left
+
+</style>
diff --git a/.vuepress/theme/components/PageNav.vue b/.vuepress/theme/components/PageNav.vue
new file mode 100644
index 0000000..4c19aae
--- /dev/null
+++ b/.vuepress/theme/components/PageNav.vue
@@ -0,0 +1,163 @@
+<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"
+        >
+          {{ prev.title || prev.path }}
+
+          <OutboundLink />
+        </a>
+
+        <RouterLink
+          v-else
+          class="prev"
+          :to="prev.path"
+        >
+          {{ 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 />
+        </a>
+
+        <RouterLink
+          v-else
+          :to="next.path"
+        >
+          {{ next.title || next.path }}
+        </RouterLink>
+        →
+      </span>
+    </p>
+  </div>
+</template>
+
+<script>
+import { resolvePage } from '../util'
+import isString from 'lodash/isString'
+import isNil from 'lodash/isNil'
+
+export default {
+  name: 'PageNav',
+
+  props: ['sidebarItems'],
+
+  computed: {
+    prev () {
+      return resolvePageLink(LINK_TYPES.PREV, this)
+    },
+
+    next () {
+      return resolvePageLink(LINK_TYPES.NEXT, this)
+    }
+  }
+}
+
+function resolvePrev (page, items) {
+  return find(page, items, -1)
+}
+
+function resolveNext (page, items) {
+  return find(page, items, 1)
+}
+
+const LINK_TYPES = {
+  NEXT: {
+    resolveLink: resolveNext,
+    getThemeLinkConfig: ({ nextLinks }) => nextLinks,
+    getPageLinkConfig: ({ frontmatter }) => frontmatter.next
+  },
+  PREV: {
+    resolveLink: resolvePrev,
+    getThemeLinkConfig: ({ prevLinks }) => prevLinks,
+    getPageLinkConfig: ({ frontmatter }) => frontmatter.prev
+  }
+}
+
+function resolvePageLink (
+  linkType,
+  { $themeConfig, $page, $route, $site, sidebarItems }
+) {
+  const { resolveLink, getThemeLinkConfig, getPageLinkConfig } = linkType
+
+  // Get link config from theme
+  const themeLinkConfig = getThemeLinkConfig($themeConfig)
+
+  // Get link config from current page
+  const pageLinkConfig = getPageLinkConfig($page)
+
+  // Page link config will overwrite global theme link config if defined
+  const link = isNil(pageLinkConfig) ? themeLinkConfig : pageLinkConfig
+
+  if (link === false) {
+    return
+  } else if (isString(link)) {
+    return resolvePage($site.pages, link, $route.path)
+  } else {
+    return resolveLink($page, sidebarItems)
+  }
+}
+
+function find (page, items, offset) {
+  const res = []
+  flatten(items, res)
+  for (let i = 0; i < res.length; i++) {
+    const cur = res[i]
+    if (cur.type === 'page' && cur.path === decodeURIComponent(page.path)) {
+      return res[i + offset]
+    }
+  }
+}
+
+function flatten (items, res) {
+  for (let i = 0, l = items.length; i < l; i++) {
+    if (items[i].type === 'group') {
+      flatten(items[i].children || [], res)
+    } else {
+      res.push(items[i])
+    }
+  }
+}
+</script>
+
+<style lang="stylus">
+@require '../styles/wrapper.styl'
+
+.page-nav
+  @extend $wrapper
+  padding-top 1rem
+  padding-bottom 0
+  .inner
+    min-height 2rem
+    margin-top 0
+    border-top 1px solid $borderColor
+    padding-top 1rem
+    overflow auto // clear float
+  .next
+    float right
+</style>
diff --git a/.vuepress/theme/components/Sidebar.vue b/.vuepress/theme/components/Sidebar.vue
new file mode 100644
index 0000000..e70e333
--- /dev/null
+++ b/.vuepress/theme/components/Sidebar.vue
@@ -0,0 +1,64 @@
+<template>
+  <aside class="sidebar">
+    <NavLinks />
+
+    <slot name="top" />
+
+    <SidebarLinks
+      :depth="0"
+      :items="items"
+    />
+    <slot name="bottom" />
+  </aside>
+</template>
+
+<script>
+import SidebarLinks from '@theme/components/SidebarLinks.vue'
+import NavLinks from '@theme/components/NavLinks.vue'
+
+export default {
+  name: 'Sidebar',
+
+  components: { SidebarLinks, NavLinks },
+
+  props: ['items']
+}
+</script>
+
+<style lang="stylus">
+.sidebar
+  ul
+    padding 0
+    margin 0
+    list-style-type none
+  a
+    display inline-block
+  .nav-links
+    display none
+    border-bottom 1px solid $borderColor
+    padding 0.5rem 0 0.75rem 0
+    a
+      font-weight 600
+    .nav-item, .repo-link
+      display block
+      line-height 1.25rem
+      font-size 1.1em
+      padding 0.5rem 0 0.5rem 1.5rem
+  & > .sidebar-links
+    padding 1.5rem 0
+    & > li > a.sidebar-link
+      font-size 1.1em
+      line-height 1.7
+      font-weight bold
+    & > li:not(:first-child)
+      margin-top .75rem
+
+@media (max-width: $MQMobile)
+  .sidebar
+    .nav-links
+      display block
+      .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
+        top calc(1rem - 2px)
+    & > .sidebar-links
+      padding 1rem 0
+</style>
diff --git a/.vuepress/theme/components/SidebarButton.vue b/.vuepress/theme/components/SidebarButton.vue
new file mode 100644
index 0000000..3f54afd
--- /dev/null
+++ b/.vuepress/theme/components/SidebarButton.vue
@@ -0,0 +1,40 @@
+<template>
+  <div
+    class="sidebar-button"
+    @click="$emit('toggle-sidebar')"
+  >
+    <svg
+      class="icon"
+      xmlns="http://www.w3.org/2000/svg"
+      aria-hidden="true"
+      role="img"
+      viewBox="0 0 448 512"
+    >
+      <path
+        fill="currentColor"
+        d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"
+        class=""
+      />
+    </svg>
+  </div>
+</template>
+
+<style lang="stylus">
+.sidebar-button
+  cursor pointer
+  display none
+  width 1.25rem
+  height 1.25rem
+  position absolute
+  padding 0.6rem
+  top 0.6rem
+  left 1rem
+  .icon
+    display block
+    width 1.25rem
+    height 1.25rem
+
+@media (max-width: $MQMobile)
+  .sidebar-button
+    display block
+</style>
diff --git a/.vuepress/theme/components/SidebarGroup.vue b/.vuepress/theme/components/SidebarGroup.vue
new file mode 100644
index 0000000..23f8a61
--- /dev/null
+++ b/.vuepress/theme/components/SidebarGroup.vue
@@ -0,0 +1,140 @@
+<template>
+  <section
+    class="sidebar-group"
+    :class="[
+      {
+        collapsable,
+        'is-sub-group': depth !== 0
+      },
+      `depth-${depth}`
+    ]"
+  >
+    <RouterLink
+      v-if="item.path"
+      class="sidebar-heading clickable"
+      :class="{
+        open,
+        'active': isActive($route, item.path)
+      }"
+      :to="item.path"
+      @click.native="$emit('toggle')"
+    >
+      <span>{{ item.title }}</span>
+      <span
+        v-if="collapsable"
+        class="arrow"
+        :class="open ? 'down' : 'right'"
+      />
+    </RouterLink>
+
+    <p
+      v-else
+      class="sidebar-heading"
+      :class="{ open }"
+      @click="$emit('toggle')"
+    >
+      <span>{{ item.title }}</span>
+      <span
+        v-if="collapsable"
+        class="arrow"
+        :class="open ? 'down' : 'right'"
+      />
+    </p>
+
+    <DropdownTransition>
+      <SidebarLinks
+        v-if="open || !collapsable"
+        class="sidebar-group-items"
+        :items="item.children"
+        :sidebar-depth="item.sidebarDepth"
+        :depth="depth + 1"
+      />
+    </DropdownTransition>
+  </section>
+</template>
+
+<script>
+import { isActive } from '../util'
+import DropdownTransition from '@theme/components/DropdownTransition.vue'
+
+export default {
+  name: 'SidebarGroup',
+
+  components: {
+    DropdownTransition
+  },
+
+  props: [
+    'item',
+    'open',
+    'collapsable',
+    'depth'
+  ],
+
+  // ref: https://vuejs.org/v2/guide/components-edge-cases.html#Circular-References-Between-Components
+  beforeCreate () {
+    this.$options.components.SidebarLinks = require('@theme/components/SidebarLinks.vue').default
+  },
+
+  methods: { isActive }
+}
+</script>
+
+<style lang="stylus">
+.sidebar-group
+  .sidebar-group
+    padding-left 0.5em
+  &:not(.collapsable)
+    .sidebar-heading:not(.clickable)
+      cursor auto
+      color inherit
+  // refine styles of nested sidebar groups
+  &.is-sub-group
+    padding-left 0
+    & > .sidebar-heading
+      font-size 0.95em
+      line-height 1.4
+      font-weight normal
+      padding-left 2rem
+      &:not(.clickable)
+        opacity 0.5
+    & > .sidebar-group-items
+      padding-left 1rem
+      & > li > .sidebar-link
+        font-size: 0.95em;
+        border-left none
+  &.depth-2
+    & > .sidebar-heading
+      border-left none
+
+.sidebar-heading
+  color $textColor
+  transition color .15s ease
+  cursor pointer
+  font-size 1.1em
+  font-weight bold
+  // text-transform uppercase
+  padding 0.35rem 1.5rem 0.35rem 1.25rem
+  width 100%
+  box-sizing border-box
+  margin 0
+  border-left 0.25rem solid transparent
+  &.open, &:hover
+    color inherit
+  .arrow
+    position relative
+    top -0.12em
+    left 0.5em
+  &.clickable
+    &.active
+      font-weight 600
+      color $accentColor
+      border-left-color $accentColor
+    &:hover
+      color $accentColor
+
+.sidebar-group-items
+  transition height .1s ease-out
+  font-size 0.95em
+  overflow hidden
+</style>
diff --git a/.vuepress/theme/components/SidebarLink.vue b/.vuepress/theme/components/SidebarLink.vue
new file mode 100644
index 0000000..4cd7665
--- /dev/null
+++ b/.vuepress/theme/components/SidebarLink.vue
@@ -0,0 +1,133 @@
+<script>
+import { isActive, hashRE, groupHeaders } from '../util'
+
+export default {
+  functional: true,
+
+  props: ['item', 'sidebarDepth'],
+
+  render (h,
+    {
+      parent: {
+        $page,
+        $site,
+        $route,
+        $themeConfig,
+        $themeLocaleConfig
+      },
+      props: {
+        item,
+        sidebarDepth
+      }
+    }) {
+    // use custom active class matching logic
+    // due to edge case of paths ending with / + hash
+    const selfActive = isActive($route, item.path)
+    // for sidebar: auto pages, a hash link should be active if one of its child
+    // matches
+    const active = item.type === 'auto'
+      ? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
+      : selfActive
+    const link = item.type === 'external'
+      ? renderExternal(h, item.path, item.title || item.path)
+      : renderLink(h, item.path, item.title || item.path, active)
+
+    const maxDepth = [
+      $page.frontmatter.sidebarDepth,
+      sidebarDepth,
+      $themeLocaleConfig.sidebarDepth,
+      $themeConfig.sidebarDepth,
+      1
+    ].find(depth => depth !== undefined)
+
+    const displayAllHeaders = $themeLocaleConfig.displayAllHeaders
+      || $themeConfig.displayAllHeaders
+
+    if (item.type === 'auto') {
+      return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
+    } else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) {
+      const children = groupHeaders(item.headers)
+      return [link, renderChildren(h, children, item.path, $route, maxDepth)]
+    } else {
+      return link
+    }
+  }
+}
+
+function renderLink (h, to, text, active, level) {
+  const component = {
+    props: {
+      to,
+      activeClass: '',
+      exactActiveClass: ''
+    },
+    class: {
+      active,
+      'sidebar-link': true
+    }
+  }
+
+  if (level > 2) {
+    component.style = {
+      'padding-left': level + 'rem'
+    }
+  }
+
+  return h('RouterLink', component, text)
+}
+
+function renderChildren (h, children, path, route, maxDepth, depth = 1) {
+  if (!children || depth > maxDepth) return null
+  return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
+    const active = isActive(route, path + '#' + c.slug)
+    return h('li', { class: 'sidebar-sub-header' }, [
+      renderLink(h, path + '#' + c.slug, c.title, active, c.level - 1),
+      renderChildren(h, c.children, path, route, maxDepth, depth + 1)
+    ])
+  }))
+}
+
+function renderExternal (h, to, text) {
+  return h('a', {
+    attrs: {
+      href: to,
+      target: '_blank',
+      rel: 'noopener noreferrer'
+    },
+    class: {
+      'sidebar-link': true
+    }
+  }, [text, h('OutboundLink')])
+}
+</script>
+
+<style lang="stylus">
+.sidebar .sidebar-sub-headers
+  padding-left 1rem
+  font-size 0.95em
+
+a.sidebar-link
+  font-size 1em
+  font-weight 400
+  display inline-block
+  color $textColor
+  border-left 0.25rem solid transparent
+  padding 0.35rem 1rem 0.35rem 1.25rem
+  line-height 1.4
+  width: 100%
+  box-sizing: border-box
+  &:hover
+    color $accentColor
+  &.active
+    font-weight 600
+    color $accentColor
+    border-left-color $accentColor
+  .sidebar-group &
+    padding-left 2rem
+  .sidebar-sub-headers &
+    padding-top 0.25rem
+    padding-bottom 0.25rem
+    border-left none
+    &.active
+      font-weight 500
+</style>
diff --git a/.vuepress/theme/components/SidebarLinks.vue b/.vuepress/theme/components/SidebarLinks.vue
new file mode 100644
index 0000000..7adf461
--- /dev/null
+++ b/.vuepress/theme/components/SidebarLinks.vue
@@ -0,0 +1,102 @@
+<template>
+  <ul
+    v-if="items.length"
+    class="sidebar-links"
+  >
+    <li
+      v-for="(item, i) in items"
+      :key="i"
+    >
+      <SidebarGroup
+        v-if="item.type === 'group'"
+        :item="item"
+        :open="i === openGroupIndex"
+        :collapsable="item.collapsable || item.collapsible"
+        :depth="depth"
+        @toggle="toggleGroup(i)"
+      />
+      <SidebarLink
+        v-else
+        :sidebar-depth="sidebarDepth"
+        :item="item"
+      />
+    </li>
+  </ul>
+</template>
+
+<script>
+import SidebarGroup from '@theme/components/SidebarGroup.vue'
+import SidebarLink from '@theme/components/SidebarLink.vue'
+import { isActive } from '../util'
+
+export default {
+  name: 'SidebarLinks',
+
+  components: { SidebarGroup, SidebarLink },
+
+  props: [
+    'items',
+    'depth',  // depth of current sidebar links
+    'sidebarDepth' // depth of headers to be extracted
+  ],
+
+  data () {
+    return {
+      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)
+    }
+  }
+}
+
+function resolveOpenGroupIndex (route, items) {
+  for (let i = 0; i < items.length; i++) {
+    const item = items[i]
+    if (descendantIsActive(route, item)) {
+      return i
+    }
+  }
+  return -1
+}
+
+function descendantIsActive (route, item) {
+  if (item.type === 'group') {
+    return item.children.some(child => {
+      if (child.type === 'group') {
+        return descendantIsActive(route, child)
+      } else {
+        return child.type === 'page' && isActive(route, child.path)
+      }
+    })
+  }
+  return false
+}
+</script>
diff --git a/.vuepress/theme/global-components/Badge.vue b/.vuepress/theme/global-components/Badge.vue
new file mode 100644
index 0000000..53951f9
--- /dev/null
+++ b/.vuepress/theme/global-components/Badge.vue
@@ -0,0 +1,44 @@
+<script>
+export default {
+  functional: true,
+  props: {
+    type: {
+      type: String,
+      default: 'tip'
+    },
+    text: String,
+    vertical: {
+      type: String,
+      default: 'top'
+    }
+  },
+  render (h, { props, slots }) {
+    return h('span', {
+      class: ['badge', props.type],
+      style: {
+        verticalAlign: props.vertical
+      }
+    }, props.text || slots().default)
+  }
+}
+</script>
+
+<style lang="stylus" scoped>
+.badge
+  display inline-block
+  font-size 14px
+  height 18px
+  line-height 18px
+  border-radius 3px
+  padding 0 6px
+  color white
+  background-color #42b983
+  &.tip, &.green
+    background-color $badgeTipColor
+  &.error
+    background-color $badgeErrorColor
+  &.warning, &.warn, &.yellow
+    background-color $badgeWarningColor
+  & + &
+    margin-left 5px
+</style>
diff --git a/.vuepress/theme/index.js b/.vuepress/theme/index.js
new file mode 100644
index 0000000..baaf102
--- /dev/null
+++ b/.vuepress/theme/index.js
@@ -0,0 +1,59 @@
+const path = require('path')
+
+// Theme API.
+module.exports = (options, ctx) => {
+  const { themeConfig, siteConfig } = ctx
+
+  // resolve algolia
+  const isAlgoliaSearch = (
+    themeConfig.algolia
+    || Object
+        .keys(siteConfig.locales && themeConfig.locales || {})
+        .some(base => themeConfig.locales[base].algolia)
+  )
+
+  const enableSmoothScroll = themeConfig.smoothScroll === true
+
+  return {
+    alias () {
+      return {
+        '@AlgoliaSearchBox': isAlgoliaSearch
+          ? path.resolve(__dirname, 'components/AlgoliaSearchBox.vue')
+          : path.resolve(__dirname, 'noopModule.js')
+      }
+    },
+
+    plugins: [
+      ['@vuepress/active-header-links', options.activeHeaderLinks],
+      '@vuepress/search',
+      '@vuepress/plugin-nprogress',
+      ['container', {
+        type: 'tip',
+        defaultTitle: {
+          '/': 'TIP',
+          '/zh/': '提示'
+        }
+      }],
+      ['container', {
+        type: 'warning',
+        defaultTitle: {
+          '/': 'WARNING',
+          '/zh/': '注意'
+        }
+      }],
+      ['container', {
+        type: 'danger',
+        defaultTitle: {
+          '/': 'WARNING',
+          '/zh/': '警告'
+        }
+      }],
+      ['container', {
+        type: 'details',
+        before: info => `<details class="custom-block details">${info ? `<summary>${info}</summary>` : ''}\n`,
+        after: () => '</details>\n'
+      }],
+      ['smooth-scroll', enableSmoothScroll]
+    ]
+  }
+}
diff --git a/.vuepress/theme/layouts/404.vue b/.vuepress/theme/layouts/404.vue
new file mode 100644
index 0000000..2cbfa0f
--- /dev/null
+++ b/.vuepress/theme/layouts/404.vue
@@ -0,0 +1,30 @@
+<template>
+  <div class="theme-container">
+    <div class="theme-default-content">
+      <h1>404</h1>
+
+      <blockquote>{{ getMsg() }}</blockquote>
+
+      <RouterLink to="/">
+        Take me home.
+      </RouterLink>
+    </div>
+  </div>
+</template>
+
+<script>
+const msgs = [
+  `There's nothing here.`,
+  `How did we get here?`,
+  `That's a Four-Oh-Four.`,
+  `Looks like we've got some broken links.`
+]
+
+export default {
+  methods: {
+    getMsg () {
+      return msgs[Math.floor(Math.random() * msgs.length)]
+    }
+  }
+}
+</script>
diff --git a/.vuepress/theme/layouts/Layout.vue b/.vuepress/theme/layouts/Layout.vue
new file mode 100644
index 0000000..3298070
--- /dev/null
+++ b/.vuepress/theme/layouts/Layout.vue
@@ -0,0 +1,151 @@
+<template>
+  <div
+    class="theme-container"
+    :class="pageClasses"
+    @touchstart="onTouchStart"
+    @touchend="onTouchEnd"
+  >
+    <Navbar
+      v-if="shouldShowNavbar"
+      @toggle-sidebar="toggleSidebar"
+    />
+
+    <div
+      class="sidebar-mask"
+      @click="toggleSidebar(false)"
+    />
+
+    <Sidebar
+      :items="sidebarItems"
+      @toggle-sidebar="toggleSidebar"
+    >
+      <template #top>
+        <slot name="sidebar-top" />
+      </template>
+      <template #bottom>
+        <slot name="sidebar-bottom" />
+      </template>
+    </Sidebar>
+
+    <Home v-if="$page.frontmatter.home" />
+
+    <Page
+      v-else
+      :sidebar-items="sidebarItems"
+    >
+      <template #top>
+        <slot name="page-top" />
+      </template>
+      <template #bottom>
+        <slot name="page-bottom" />
+      </template>
+    </Page>
+  </div>
+</template>
+
+<script>
+import Home from '@theme/components/Home.vue'
+import Navbar from '@theme/components/Navbar.vue'
+import Page from '@theme/components/Page.vue'
+import Sidebar from '@theme/components/Sidebar.vue'
+import { resolveSidebarItems } from '../util'
+
+export default {
+  name: 'Layout',
+
+  components: {
+    Home,
+    Page,
+    Sidebar,
+    Navbar
+  },
+
+  data () {
+    return {
+      isSidebarOpen: false
+    }
+  },
+
+  computed: {
+    shouldShowNavbar () {
+      const { themeConfig } = this.$site
+      const { frontmatter } = this.$page
+      if (
+        frontmatter.navbar === false
+        || themeConfig.navbar === false) {
+        return false
+      }
+      return (
+        this.$title
+        || themeConfig.logo
+        || themeConfig.repo
+        || themeConfig.nav
+        || this.$themeLocaleConfig.nav
+      )
+    },
+
+    shouldShowSidebar () {
+      const { frontmatter } = this.$page
+      return (
+        !frontmatter.home
+        && frontmatter.sidebar !== false
+        && this.sidebarItems.length
+      )
+    },
+
+    sidebarItems () {
+      return resolveSidebarItems(
+        this.$page,
+        this.$page.regularPath,
+        this.$site,
+        this.$localePath
+      )
+    },
+
+    pageClasses () {
+      const userPageClass = this.$page.frontmatter.pageClass
+      return [
+        {
+          'no-navbar': !this.shouldShowNavbar,
+          'sidebar-open': this.isSidebarOpen,
+          'no-sidebar': !this.shouldShowSidebar
+        },
+        userPageClass
+      ]
+    }
+  },
+
+  mounted () {
+    this.$router.afterEach(() => {
+      this.isSidebarOpen = false
+    })
+  },
+
+  methods: {
+    toggleSidebar (to) {
+      this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
+      this.$emit('toggle-sidebar', this.isSidebarOpen)
+    },
+
+    // side swipe
+    onTouchStart (e) {
+      this.touchStart = {
+        x: e.changedTouches[0].clientX,
+        y: e.changedTouches[0].clientY
+      }
+    },
+
+    onTouchEnd (e) {
+      const dx = e.changedTouches[0].clientX - this.touchStart.x
+      const dy = e.changedTouches[0].clientY - this.touchStart.y
+      if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
+        if (dx > 0 && this.touchStart.x <= 80) {
+          this.toggleSidebar(true)
+        } else {
+          this.toggleSidebar(false)
+        }
+      }
+    }
+  }
+}
+</script>
diff --git a/.vuepress/theme/noopModule.js b/.vuepress/theme/noopModule.js
new file mode 100644
index 0000000..b1c6ea4
--- /dev/null
+++ b/.vuepress/theme/noopModule.js
@@ -0,0 +1 @@
+export default {}
diff --git a/.vuepress/theme/styles/arrow.styl b/.vuepress/theme/styles/arrow.styl
new file mode 100644
index 0000000..20bffc0
--- /dev/null
+++ b/.vuepress/theme/styles/arrow.styl
@@ -0,0 +1,22 @@
+@require './config'
+
+.arrow
+  display inline-block
+  width 0
+  height 0
+  &.up
+    border-left 4px solid transparent
+    border-right 4px solid transparent
+    border-bottom 6px solid $arrowBgColor
+  &.down
+    border-left 4px solid transparent
+    border-right 4px solid transparent
+    border-top 6px solid $arrowBgColor
+  &.right
+    border-top 4px solid transparent
+    border-bottom 4px solid transparent
+    border-left 6px solid $arrowBgColor
+  &.left
+    border-top 4px solid transparent
+    border-bottom 4px solid transparent
+    border-right 6px solid $arrowBgColor
diff --git a/.vuepress/theme/styles/code.styl b/.vuepress/theme/styles/code.styl
new file mode 100644
index 0000000..9d3aa9a
--- /dev/null
+++ b/.vuepress/theme/styles/code.styl
@@ -0,0 +1,137 @@
+{$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'
diff --git a/.vuepress/theme/styles/config.styl b/.vuepress/theme/styles/config.styl
new file mode 100644
index 0000000..9e40321
--- /dev/null
+++ b/.vuepress/theme/styles/config.styl
@@ -0,0 +1 @@
+$contentClass = '.theme-default-content'
diff --git a/.vuepress/theme/styles/custom-blocks.styl b/.vuepress/theme/styles/custom-blocks.styl
new file mode 100644
index 0000000..5b86816
--- /dev/null
+++ b/.vuepress/theme/styles/custom-blocks.styl
@@ -0,0 +1,44 @@
+.custom-block
+  .custom-block-title
+    font-weight 600
+    margin-bottom -0.4rem
+  &.tip, &.warning, &.danger
+    padding .1rem 1.5rem
+    border-left-width .5rem
+    border-left-style solid
+    margin 1rem 0
+  &.tip
+    background-color #f3f5f7
+    border-color #42b983
+  &.warning
+    background-color rgba(255,229,100,.3)
+    border-color darken(#ffe564, 35%)
+    color darken(#ffe564, 70%)
+    .custom-block-title
+      color darken(#ffe564, 50%)
+    a
+      color $textColor
+  &.danger
+    background-color #ffe6e6
+    border-color darken(red, 20%)
+    color darken(red, 70%)
+    .custom-block-title
+      color darken(red, 40%)
+    a
+      color $textColor
+  &.details
+    display block
+    position relative
+    border-radius 2px
+    margin 1.6em 0
+    padding 1.6em
+    background-color #eee
+    h4
+      margin-top 0
+    figure, p
+      &:last-child
+        margin-bottom 0
+        padding-bottom 0
+    summary
+      outline none
+      cursor pointer
diff --git a/.vuepress/theme/styles/index.styl b/.vuepress/theme/styles/index.styl
new file mode 100644
index 0000000..976bfb0
--- /dev/null
+++ b/.vuepress/theme/styles/index.styl
@@ -0,0 +1,201 @@
+@require './config'
+@require './code'
+@require './custom-blocks'
+@require './arrow'
+@require './wrapper'
+@require './toc'
+
+html, body
+  padding 0
+  margin 0
+  background-color #fff
+
+body
+  font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif
+  -webkit-font-smoothing antialiased
+  -moz-osx-font-smoothing grayscale
+  font-size 16px
+  color $textColor
+
+.page
+  padding-left $sidebarWidth
+
+.navbar
+  position fixed
+  z-index 20
+  top 0
+  left 0
+  right 0
+  height $navbarHeight
+  background-color #fff
+  box-sizing border-box
+  border-bottom 1px solid $borderColor
+
+.sidebar-mask
+  position fixed
+  z-index 9
+  top 0
+  left 0
+  width 100vw
+  height 100vh
+  display none
+
+.sidebar
+  font-size 16px
+  background-color #fff
+  width $sidebarWidth
+  position fixed
+  z-index 10
+  margin 0
+  top $navbarHeight
+  left 0
+  bottom 0
+  box-sizing border-box
+  border-right 1px solid $borderColor
+  overflow-y auto
+
+{$contentClass}:not(.custom)
+  @extend $wrapper
+  > *:first-child
+    margin-top $navbarHeight
+
+  a:hover
+    text-decoration underline
+
+  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 $accentColor
+  text-decoration none
+
+p a code
+  font-weight 400
+  color $accentColor
+
+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 #999;
+  border-left .2rem solid #dfe2e5
+  margin 1rem 0
+  padding .25rem 0 .25rem 1rem
+
+  & > p
+    margin 0
+
+ul, ol
+  padding-left 1.2em
+
+strong
+  font-weight 600
+
+h1, h2, h3, h4, h5, h6
+  font-weight 600
+  line-height 1.25
+
+  {$contentClass}:not(.custom) > &
+    margin-top (0.5rem - $navbarHeight)
+    padding-top ($navbarHeight + 1rem)
+    margin-bottom 0
+
+    &:first-child
+      margin-top -1.5rem
+      margin-bottom 1rem
+
+      + p, + pre, + .custom-block
+        margin-top 2rem
+
+  &:hover .header-anchor
+    opacity: 1
+
+h1
+  font-size 2.2rem
+
+h2
+  font-size 1.65rem
+  padding-bottom .3rem
+  border-bottom 1px solid $borderColor
+
+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
+
+  &: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 $borderColor
+
+table
+  border-collapse collapse
+  margin 1rem 0
+  display: block
+  overflow-x: auto
+
+tr
+  border-top 1px solid #dfe2e5
+
+  &:nth-child(2n)
+    background-color #f6f8fa
+
+th, td
+  border 1px solid #dfe2e5
+  padding .6em 1em
+
+.theme-container
+  &.sidebar-open
+    .sidebar-mask
+      display: block
+
+  &.no-navbar
+    {$contentClass}:not(.custom) > h1, h2, h3, h4, h5, h6
+      margin-top 1.5rem
+      padding-top 0
+
+    .sidebar
+      top 0
+
+
+@media (min-width: ($MQMobile + 1px))
+  .theme-container.no-sidebar
+    .sidebar
+      display none
+
+    .page
+      padding-left 0
+
+@require 'mobile.styl'
diff --git a/.vuepress/theme/styles/mobile.styl b/.vuepress/theme/styles/mobile.styl
new file mode 100644
index 0000000..f5bd327
--- /dev/null
+++ b/.vuepress/theme/styles/mobile.styl
@@ -0,0 +1,37 @@
+@require './config'
+
+$mobileSidebarWidth = $sidebarWidth * 0.82
+
+// narrow desktop / iPad
+@media (max-width: $MQNarrow)
+  .sidebar
+    font-size 15px
+    width $mobileSidebarWidth
+  .page
+    padding-left $mobileSidebarWidth
+
+// wide mobile
+@media (max-width: $MQMobile)
+  .sidebar
+    top 0
+    padding-top $navbarHeight
+    transform translateX(-100%)
+    transition transform .2s ease
+  .page
+    padding-left 0
+  .theme-container
+    &.sidebar-open
+      .sidebar
+        transform translateX(0)
+    &.no-navbar
+      .sidebar
+        padding-top: 0
+
+// narrow mobile
+@media (max-width: $MQMobileNarrow)
+  h1
+    font-size 1.9rem
+  {$contentClass}
+    div[class*="language-"]
+      margin 0.85rem -1.5rem
+      border-radius 0
diff --git a/.vuepress/theme/styles/toc.styl b/.vuepress/theme/styles/toc.styl
new file mode 100644
index 0000000..d3e7106
--- /dev/null
+++ b/.vuepress/theme/styles/toc.styl
@@ -0,0 +1,3 @@
+.table-of-contents
+  .badge
+    vertical-align middle
diff --git a/.vuepress/theme/styles/wrapper.styl b/.vuepress/theme/styles/wrapper.styl
new file mode 100644
index 0000000..a99262c
--- /dev/null
+++ b/.vuepress/theme/styles/wrapper.styl
@@ -0,0 +1,9 @@
+$wrapper
+  max-width $contentWidth
+  margin 0 auto
+  padding 2rem 2.5rem
+  @media (max-width: $MQNarrow)
+    padding 2rem
+  @media (max-width: $MQMobileNarrow)
+    padding 1.5rem
+
diff --git a/.vuepress/theme/util/index.js b/.vuepress/theme/util/index.js
new file mode 100644
index 0000000..23e78f8
--- /dev/null
+++ b/.vuepress/theme/util/index.js
@@ -0,0 +1,240 @@
+export const hashRE = /#.*$/
+export const extRE = /\.(md|html)$/
+export const endingSlashRE = /\/$/
+export const outboundRE = /^[a-z]+:/i
+
+export function normalize (path) {
+  return decodeURI(path)
+    .replace(hashRE, '')
+    .replace(extRE, '')
+}
+
+export function getHash (path) {
+  const match = path.match(hashRE)
+  if (match) {
+    return match[0]
+  }
+}
+
+export function isExternal (path) {
+  return outboundRE.test(path)
+}
+
+export function isMailto (path) {
+  return /^mailto:/.test(path)
+}
+
+export function isTel (path) {
+  return /^tel:/.test(path)
+}
+
+export function ensureExt (path) {
+  if (isExternal(path)) {
+    return path
+  }
+  const hashMatch = path.match(hashRE)
+  const hash = hashMatch ? hashMatch[0] : ''
+  const normalized = normalize(path)
+
+  if (endingSlashRE.test(normalized)) {
+    return path
+  }
+  return normalized + '.html' + hash
+}
+
+export function isActive (route, path) {
+  const routeHash = decodeURIComponent(route.hash)
+  const linkHash = getHash(path)
+  if (linkHash && routeHash !== linkHash) {
+    return false
+  }
+  const routePath = normalize(route.path)
+  const pagePath = normalize(path)
+  return routePath === pagePath
+}
+
+export function resolvePage (pages, rawPath, base) {
+  if (isExternal(rawPath)) {
+    return {
+      type: 'external',
+      path: rawPath
+    }
+  }
+  if (base) {
+    rawPath = resolvePath(rawPath, base)
+  }
+  const path = normalize(rawPath)
+  for (let i = 0; i < pages.length; i++) {
+    if (normalize(pages[i].regularPath) === path) {
+      return Object.assign({}, pages[i], {
+        type: 'page',
+        path: ensureExt(pages[i].path)
+      })
+    }
+  }
+  console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`)
+  return {}
+}
+
+function resolvePath (relative, base, append) {
+  const firstChar = relative.charAt(0)
+  if (firstChar === '/') {
+    return relative
+  }
+
+  if (firstChar === '?' || firstChar === '#') {
+    return base + relative
+  }
+
+  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 = relative.replace(/^\//, '').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('/')
+}
+
+/**
+ * @param { Page } page
+ * @param { string } regularPath
+ * @param { SiteData } site
+ * @param { string } localePath
+ * @returns { SidebarGroup }
+ */
+export function resolveSidebarItems (page, regularPath, site, localePath) {
+  const { pages, themeConfig } = site
+
+  const localeConfig = localePath && themeConfig.locales
+    ? themeConfig.locales[localePath] || themeConfig
+    : themeConfig
+
+  const pageSidebarConfig = page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar
+  if (pageSidebarConfig === 'auto') {
+    return resolveHeaders(page)
+  }
+
+  const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar
+  if (!sidebarConfig) {
+    return []
+  } else {
+    const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig)
+    return config
+      ? config.map(item => resolveItem(item, pages, base))
+      : []
+  }
+}
+
+/**
+ * @param { Page } page
+ * @returns { SidebarGroup }
+ */
+function resolveHeaders (page) {
+  const headers = groupHeaders(page.headers || [])
+  return [{
+    type: 'group',
+    collapsable: false,
+    title: page.title,
+    path: null,
+    children: headers.map(h => ({
+      type: 'auto',
+      title: h.title,
+      basePath: page.path,
+      path: page.path + '#' + h.slug,
+      children: h.children || []
+    }))
+  }]
+}
+
+export function groupHeaders (headers) {
+  // group h3s under h2
+  headers = headers.map(h => Object.assign({}, h))
+  let lastH2
+  headers.forEach(h => {
+    if (h.level === 2) {
+      lastH2 = h
+    } else if (lastH2) {
+      (lastH2.children || (lastH2.children = [])).push(h)
+    }
+  })
+  return headers.filter(h => h.level === 2)
+}
+
+export function resolveNavLinkItem (linkItem) {
+  return Object.assign(linkItem, {
+    type: linkItem.items && linkItem.items.length ? 'links' : 'link'
+  })
+}
+
+/**
+ * @param { Route } route
+ * @param { Array<string|string[]> | Array<SidebarGroup> | [link: string]: SidebarConfig } config
+ * @returns { base: string, config: SidebarConfig }
+ */
+export function resolveMatchingConfig (regularPath, config) {
+  if (Array.isArray(config)) {
+    return {
+      base: '/',
+      config: config
+    }
+  }
+  for (const base in config) {
+    if (ensureEndingSlash(regularPath).indexOf(encodeURI(base)) === 0) {
+      return {
+        base,
+        config: config[base]
+      }
+    }
+  }
+  return {}
+}
+
+function ensureEndingSlash (path) {
+  return /(\.html|\/)$/.test(path)
+    ? path
+    : path + '/'
+}
+
+function resolveItem (item, pages, base, groupDepth = 1) {
+  if (typeof item === 'string') {
+    return resolvePage(pages, item, base)
+  } else if (Array.isArray(item)) {
+    return Object.assign(resolvePage(pages, item[0], base), {
+      title: item[1]
+    })
+  } else {
+    const children = item.children || []
+    if (children.length === 0 && item.path) {
+      return Object.assign(resolvePage(pages, item.path, base), {
+        title: item.title
+      })
+    }
+    return {
+      type: 'group',
+      path: item.path,
+      title: item.title,
+      sidebarDepth: item.sidebarDepth,
+      children: children.map(child => resolveItem(child, pages, base, groupDepth + 1)),
+      collapsable: item.collapsable !== false
+    }
+  }
+}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..13a01d6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,3 @@
+all:
+	git clone https://github.com/apache/incubator-teaclave.git teaclave || cd teaclave && git pull
+	vuepress build -d dist
diff --git a/index.md b/index.md
index 93e3eac..8e60ba9 100644
--- a/index.md
+++ b/index.md
@@ -2,11 +2,8 @@
 home: true
 heroText: Apache Teaclave (incubating)
 tagline:  an open source universal secure computing platform, making computation on privacy-sensitive data safe and simple
-footer: "Apache Teaclave is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the Apache Incubator. Incubation is required of all newly accepted projects until a further review indicates that the infrastructure, communications, and decision making process have stabilized in a manner consistent with other successful ASF projects. While incubation status is not necessarily a reflection of the completeness or stability of the code, it does indicate that t [...]
 ---
 
-Teaclave is an open source universal secure computing platform, making computation on privacy-sensitive data safe and simple.
-
 ## Highlights
 
 - **Security**:


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@teaclave.apache.org
For additional commands, e-mail: commits-help@teaclave.apache.org