mirror of
https://github.com/pterodactyl/documentation.git
synced 2025-12-11 14:00:27 -06:00
Merge branch 'master' of https://github.com/pterodactyl/documentation
This commit is contained in:
commit
11461e29a0
@ -2,7 +2,23 @@ module.exports = {
|
|||||||
base: '/',
|
base: '/',
|
||||||
title: 'Pterodactyl',
|
title: 'Pterodactyl',
|
||||||
description: 'The open-source server management solution.',
|
description: 'The open-source server management solution.',
|
||||||
ga: 'UA-87324178-1',
|
plugins: [
|
||||||
|
['@vuepress/google-analytics', {
|
||||||
|
ga: 'UA-12345678-9'
|
||||||
|
},],
|
||||||
|
['@vuepress/search', {
|
||||||
|
searchMaxSuggestions: 10
|
||||||
|
}],
|
||||||
|
['vuepress-plugin-container', {
|
||||||
|
type: 'warning',
|
||||||
|
}],
|
||||||
|
['vuepress-plugin-container', {
|
||||||
|
type: 'tip',
|
||||||
|
}],
|
||||||
|
['vuepress-plugin-container', {
|
||||||
|
type: 'danger',
|
||||||
|
}],
|
||||||
|
],
|
||||||
configureWebpack: {
|
configureWebpack: {
|
||||||
serve: {
|
serve: {
|
||||||
hot: {
|
hot: {
|
||||||
@ -111,24 +127,63 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
title: 'Panel',
|
title: 'Panel',
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
children: [
|
path: '/panel/',
|
||||||
'/panel/getting_started',
|
currentVersion: '0.7',
|
||||||
'/panel/webserver_configuration',
|
versions: [
|
||||||
'/panel/upgrading',
|
{
|
||||||
'/panel/configuration',
|
name: '0.7',
|
||||||
'/panel/troubleshooting',
|
status: 'stable',
|
||||||
|
children: [
|
||||||
|
'/getting_started',
|
||||||
|
'/webserver_configuration',
|
||||||
|
'/upgrading',
|
||||||
|
'/configuration',
|
||||||
|
'/troubleshooting',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '1.0',
|
||||||
|
status: 'beta',
|
||||||
|
children: [
|
||||||
|
'/getting_started',
|
||||||
|
'/webserver_configuration'
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Daemon',
|
title: 'Daemon',
|
||||||
collapsable: false,
|
collapsable: false,
|
||||||
children: [
|
path: '/daemon/',
|
||||||
'/daemon/installing',
|
currentVersion: '0.6',
|
||||||
'/daemon/upgrading',
|
versions: [
|
||||||
'/daemon/configuration',
|
{
|
||||||
'/daemon/kernel_modifications',
|
name: '0.6',
|
||||||
'/daemon/debian_8_docker',
|
children: [
|
||||||
'/daemon/standalone_sftp',
|
'/installing',
|
||||||
|
'/upgrading',
|
||||||
|
'/configuration',
|
||||||
|
'/kernel_modifications',
|
||||||
|
'/debian_8_docker',
|
||||||
|
'/standalone_sftp',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Wings',
|
||||||
|
collapsable: false,
|
||||||
|
path: '/wings/',
|
||||||
|
currentVersion: '',
|
||||||
|
versions: [
|
||||||
|
{
|
||||||
|
name: '1.0',
|
||||||
|
status: 'beta',
|
||||||
|
children: [
|
||||||
|
'/installing',
|
||||||
|
'/upgrading',
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
41
.vuepress/enhanceApp.js
Normal file
41
.vuepress/enhanceApp.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
export default ({
|
||||||
|
Vue, // the version of Vue being used in the VuePress app
|
||||||
|
options, // the options for the root Vue instance
|
||||||
|
router, // the router instance for the app
|
||||||
|
siteData, // site metadata
|
||||||
|
isServer // is this enhancement applied in server-rendering or client
|
||||||
|
}) => {
|
||||||
|
findVersionedPaths(siteData.themeConfig.sidebar).forEach(vp => {
|
||||||
|
router.addRoutes(
|
||||||
|
router.options.routes.map(route => {
|
||||||
|
if (route.path.startsWith(vp.path + vp.currentVersion)) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
path: route.path.replace(vp.currentVersion, "current"),
|
||||||
|
redirect: route.path
|
||||||
|
}, {
|
||||||
|
path: route.path.replace(vp.currentVersion + "/", ""),
|
||||||
|
redirect: route.path
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}).filter(x => x).flat()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function findVersionedPaths(paths) {
|
||||||
|
return Object.entries(paths).map(([path, children]) => {
|
||||||
|
return children
|
||||||
|
.filter(child => Array.isArray(child.versions))
|
||||||
|
.map(child => ({ ...child, path: pathJoin(path, child.path) }))
|
||||||
|
}).flat()
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/29855282/4430124
|
||||||
|
function pathJoin(...parts) {
|
||||||
|
var separator = '/';
|
||||||
|
var replace = new RegExp(separator + '{1,}', 'g');
|
||||||
|
return parts.join(separator).replace(replace, separator);
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@
|
|||||||
<div class="container z-10">
|
<div class="container z-10">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div>
|
<div>
|
||||||
<img class="max-w-xl w-full" src="https://cdn.pterodactyl.io/logos/new/pterodactyl_logo_transparent.png" alt="Pterodactyl">
|
<img class="max-w-xl w-full inline-block" src="https://cdn.pterodactyl.io/logos/new/pterodactyl_logo_transparent.png" alt="Pterodactyl">
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4">
|
<div class="mt-4">
|
||||||
<a class="btn hidden md:inline-block" href="https://demo.pterodactyl.io" target="_blank" rel="nofollow noopener">Demo</a>
|
<a class="btn hidden md:inline-block" href="https://demo.pterodactyl.io" target="_blank" rel="nofollow noopener">Demo</a>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
while exposing a beautiful and intuitive UI to administrators and users. Stop wasting time fiddling
|
while exposing a beautiful and intuitive UI to administrators and users. Stop wasting time fiddling
|
||||||
with other systems — make game servers a first class citizen on your platform.
|
with other systems — make game servers a first class citizen on your platform.
|
||||||
</h3>
|
</h3>
|
||||||
<img class="max-w-lg w-full m-4" :src="$withBase('frontpage/mockup-macbook-grey.png')"></div>
|
<img class="max-w-lg w-full m-4 inline-block" :src="$withBase('frontpage/mockup-macbook-grey.png')"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="section bg-blue text-grey-lightest">
|
<div class="section bg-blue text-grey-lightest">
|
||||||
<div class="container text-center">
|
<div class="container text-center">
|
||||||
@ -116,7 +116,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="footer">MIT Licensed | Copyright © 2015 - 2019 Dane Everitt & Contributors.</div>
|
<div class="footer">MIT Licensed | Copyright © 2015 - 2020 Dane Everitt & Contributors.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,133 +1,135 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="theme-container"
|
<div
|
||||||
:class="pageClasses"
|
class="theme-container"
|
||||||
@touchstart="onTouchStart"
|
:class="pageClasses"
|
||||||
@touchend="onTouchEnd">
|
@touchstart="onTouchStart"
|
||||||
<Navbar v-if="shouldShowNavbar" @toggle-sidebar="toggleSidebar"/>
|
@touchend="onTouchEnd"
|
||||||
<div class="sidebar-mask" @click="toggleSidebar(false)"></div>
|
>
|
||||||
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar">
|
<Navbar v-if="shouldShowNavbar" @toggle-sidebar="toggleSidebar" />
|
||||||
<slot name="sidebar-top" slot="top"/>
|
<div class="sidebar-mask" @click="toggleSidebar(false)"></div>
|
||||||
<slot name="sidebar-bottom" slot="bottom"/>
|
<Sidebar :items="sidebarItems" @toggle-sidebar="toggleSidebar">
|
||||||
</Sidebar>
|
<slot name="sidebar-top" slot="top" />
|
||||||
<div class="custom-layout" v-if="$page.frontmatter.layout">
|
<slot name="sidebar-bottom" slot="bottom" />
|
||||||
<component :is="$page.frontmatter.layout"/>
|
</Sidebar>
|
||||||
</div>
|
<div class="custom-layout" v-if="$page.frontmatter.layout">
|
||||||
<Home v-else-if="$page.frontmatter.home"/>
|
<component :is="$page.frontmatter.layout" />
|
||||||
<Page v-else :sidebar-items="sidebarItems">
|
|
||||||
<slot name="page-top" slot="top"/>
|
|
||||||
<slot name="page-bottom" slot="bottom"/>
|
|
||||||
</Page>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Home v-else-if="$page.frontmatter.home" />
|
||||||
|
<Page v-else :sidebar-items="sidebarItems">
|
||||||
|
<slot name="page-top" slot="top" />
|
||||||
|
<slot name="page-bottom" slot="bottom" />
|
||||||
|
</Page>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Vue from 'vue';
|
import Vue from "vue";
|
||||||
import nprogress from 'nprogress';
|
import nprogress from "nprogress";
|
||||||
import Home from './Home.vue';
|
import Home from "./Home.vue";
|
||||||
import Navbar from './Navbar.vue';
|
import Navbar from "./Navbar.vue";
|
||||||
import Page from './Page.vue';
|
import Page from "./Page.vue";
|
||||||
import Sidebar from './Sidebar.vue';
|
import Sidebar from "./Sidebar.vue";
|
||||||
import { resolveSidebarItems } from './util';
|
import { resolveSidebarItems } from "./util";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { Home, Page, Sidebar, Navbar },
|
components: { Home, Page, Sidebar, Navbar },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isSidebarOpen: false
|
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.layout &&
|
|
||||||
! frontmatter.home &&
|
|
||||||
frontmatter.sidebar !== false &&
|
|
||||||
this.sidebarItems.length
|
|
||||||
);
|
|
||||||
},
|
|
||||||
sidebarItems() {
|
|
||||||
return resolveSidebarItems(
|
|
||||||
this.$page,
|
|
||||||
this.$route,
|
|
||||||
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() {
|
|
||||||
window.addEventListener('scroll', this.onScroll);
|
|
||||||
|
|
||||||
// configure progress bar
|
|
||||||
nprogress.configure({ showSpinner: false });
|
|
||||||
|
|
||||||
this.$router.beforeEach((to, from, next) => {
|
|
||||||
if (to.path !== from.path && ! Vue.component(to.name)) {
|
|
||||||
nprogress.start();
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$router.afterEach(() => {
|
|
||||||
nprogress.done();
|
|
||||||
this.isSidebarOpen = false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
toggleSidebar(to) {
|
|
||||||
this.isSidebarOpen = typeof to === 'boolean' ? to : ! 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
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.layout &&
|
||||||
|
!frontmatter.home &&
|
||||||
|
frontmatter.sidebar !== false &&
|
||||||
|
this.sidebarItems.length
|
||||||
|
);
|
||||||
|
},
|
||||||
|
sidebarItems() {
|
||||||
|
return resolveSidebarItems(
|
||||||
|
this.$page,
|
||||||
|
this.$route,
|
||||||
|
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() {
|
||||||
|
window.addEventListener("scroll", this.onScroll);
|
||||||
|
|
||||||
|
// configure progress bar
|
||||||
|
nprogress.configure({ showSpinner: false });
|
||||||
|
|
||||||
|
this.$router.beforeEach((to, from, next) => {
|
||||||
|
if (to.path !== from.path && !Vue.component(to.name)) {
|
||||||
|
nprogress.start();
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$router.afterEach(() => {
|
||||||
|
nprogress.done();
|
||||||
|
this.isSidebarOpen = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleSidebar(to) {
|
||||||
|
this.isSidebarOpen = typeof to === "boolean" ? to : !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>
|
</script>
|
||||||
|
|
||||||
<style src="prismjs/themes/prism-tomorrow.css"></style>
|
<style src="prismjs/themes/prism-tomorrow.css"></style>
|
||||||
<style src="./styles/main.css" lang="postcss"></style>
|
<style lang="postcss">
|
||||||
|
@import "./styles/main.css";
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,42 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<header class="nav">
|
<header class="nav">
|
||||||
<SidebarButton class="block md:hidden flex-no-shrink" @toggle-sidebar="$emit('toggle-sidebar')"/>
|
<SidebarButton
|
||||||
<div class="logo-container">
|
class="block md:hidden flex-no-shrink"
|
||||||
<router-link :to="$localePath" class="home-link">
|
@toggle-sidebar="$emit('toggle-sidebar')"
|
||||||
<img class="logo"
|
/>
|
||||||
v-if="$site.themeConfig.logo"
|
<div class="logo-container">
|
||||||
:src="$withBase($site.themeConfig.logo)">
|
<router-link :to="$localePath" class="home-link">
|
||||||
<span class="site-name hidden md:inline"
|
<img class="logo" v-if="$site.themeConfig.logo" :src="$withBase($site.themeConfig.logo)" />
|
||||||
v-if="$siteTitle"
|
<span
|
||||||
:class="{ 'can-hide': $site.themeConfig.logo }">
|
class="site-name hidden md:inline"
|
||||||
{{ $siteTitle }}
|
v-if="$siteTitle"
|
||||||
</span>
|
:class="{ 'can-hide': $site.themeConfig.logo }"
|
||||||
</router-link>
|
>{{ $siteTitle }}</span>
|
||||||
</div>
|
</router-link>
|
||||||
<div class="w-full">
|
</div>
|
||||||
<div class="flex">
|
<div class="w-full">
|
||||||
<SearchBox/>
|
<div class="flex">
|
||||||
<NavLinks class="hidden md:flex"/>
|
<SearchBox />
|
||||||
</div>
|
<NavLinks class="hidden md:flex" />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</div>
|
||||||
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SidebarButton from './SidebarButton.vue';
|
import SidebarButton from "./SidebarButton.vue";
|
||||||
import AlgoliaSearchBox from '@AlgoliaSearchBox';
|
import SearchBox from "./SearchBox.vue";
|
||||||
import SearchBox from './SearchBox.vue';
|
import NavLinks from "./NavLinks.vue";
|
||||||
import NavLinks from './NavLinks.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox },
|
components: { SidebarButton, NavLinks, SearchBox },
|
||||||
computed: {
|
computed: {}
|
||||||
algolia() {
|
};
|
||||||
return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {};
|
|
||||||
},
|
|
||||||
isAlgoliaSearch() {
|
|
||||||
return this.algolia && this.algolia.apiKey && this.algolia.indexName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,156 +1,155 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page">
|
<div class="page">
|
||||||
<slot name="top"/>
|
<slot name="top" />
|
||||||
<Content :custom="false"/>
|
<Content class="content" :custom="false" />
|
||||||
<div class="page-edit">
|
<div class="page-edit">
|
||||||
<div class="edit-link" v-if="editLink">
|
<div class="edit-link" v-if="editLink">
|
||||||
<a :href="editLink" target="_blank" rel="noopener noreferrer">{{ editLinkText }}</a>
|
<a :href="editLink" target="_blank" rel="noopener noreferrer">{{ editLinkText }}</a>
|
||||||
<OutboundLink/>
|
<OutboundLink />
|
||||||
</div>
|
</div>
|
||||||
<div class="last-updated" v-if="lastUpdated">
|
<div class="last-updated" v-if="lastUpdated">
|
||||||
<span class="prefix">{{ lastUpdatedText }}: </span>
|
<span class="prefix">{{ lastUpdatedText }}:</span>
|
||||||
<span class="time">{{ lastUpdated }}</span>
|
<span class="time">{{ lastUpdated }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<div class="page-nav" v-if="prev || next">
|
|
||||||
<p class="inner"></p>
|
|
||||||
<div class="prev">
|
|
||||||
<span v-if="prev">
|
|
||||||
← <router-link v-if="prev" :to="prev.path">{{ prev.title || prev.path }}</router-link>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="next">
|
|
||||||
<span v-if="next">
|
|
||||||
<router-link v-if="next" :to="next.path">{{ next.title || next.path }}</router-link> →
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<slot name="bottom"/>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="page-nav" v-if="prev || next">
|
||||||
|
<p class="inner"></p>
|
||||||
|
<div class="prev">
|
||||||
|
<span v-if="prev">
|
||||||
|
←
|
||||||
|
<router-link v-if="prev" :to="prev.path">{{ prev.title || prev.path }}</router-link>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="next">
|
||||||
|
<span v-if="next">
|
||||||
|
<router-link v-if="next" :to="next.path">{{ next.title || next.path }}</router-link>→
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<slot name="bottom" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { resolvePage, normalize, outboundRE, endingSlashRE } from './util';
|
import { resolvePage, normalize, outboundRE, endingSlashRE } from "./util";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: ['sidebarItems'],
|
props: ["sidebarItems"],
|
||||||
computed: {
|
computed: {
|
||||||
lastUpdated() {
|
lastUpdated() {
|
||||||
if (this.$page.lastUpdated) {
|
if (this.$page.lastUpdated) {
|
||||||
return new Date(this.$page.lastUpdated).toLocaleString(this.$lang);
|
return new Date(this.$page.lastUpdated).toLocaleString(this.$lang);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lastUpdatedText() {
|
lastUpdatedText() {
|
||||||
if (typeof this.$themeLocaleConfig.lastUpdated === 'string') {
|
if (typeof this.$themeLocaleConfig.lastUpdated === "string") {
|
||||||
return this.$themeLocaleConfig.lastUpdated;
|
return this.$themeLocaleConfig.lastUpdated;
|
||||||
}
|
}
|
||||||
if (typeof this.$site.themeConfig.lastUpdated === 'string') {
|
if (typeof this.$site.themeConfig.lastUpdated === "string") {
|
||||||
return this.$site.themeConfig.lastUpdated;
|
return this.$site.themeConfig.lastUpdated;
|
||||||
}
|
}
|
||||||
return 'Last Updated';
|
return "Last Updated";
|
||||||
},
|
},
|
||||||
prev() {
|
prev() {
|
||||||
const prev = this.$page.frontmatter.prev;
|
const prev = this.$page.frontmatter.prev;
|
||||||
if (prev === false) {
|
if (prev === false) {
|
||||||
return;
|
return;
|
||||||
} else if (prev) {
|
} else if (prev) {
|
||||||
return resolvePage(this.$site.pages, prev, this.$route.path);
|
return resolvePage(this.$site.pages, prev, this.$route.path);
|
||||||
} else {
|
} else {
|
||||||
return resolvePrev(this.$page, this.sidebarItems);
|
return resolvePrev(this.$page, this.sidebarItems);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
next() {
|
next() {
|
||||||
const next = this.$page.frontmatter.next;
|
const next = this.$page.frontmatter.next;
|
||||||
if (next === false) {
|
if (next === false) {
|
||||||
return;
|
return;
|
||||||
} else if (next) {
|
} else if (next) {
|
||||||
return resolvePage(this.$site.pages, next, this.$route.path);
|
return resolvePage(this.$site.pages, next, this.$route.path);
|
||||||
} else {
|
} else {
|
||||||
return resolveNext(this.$page, this.sidebarItems);
|
return resolveNext(this.$page, this.sidebarItems);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editLink() {
|
editLink() {
|
||||||
if (this.$page.frontmatter.editLink === false) {
|
if (this.$page.frontmatter.editLink === false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
repo,
|
repo,
|
||||||
editLinks,
|
editLinks,
|
||||||
docsDir = '',
|
docsDir = "",
|
||||||
docsBranch = 'master',
|
docsBranch = "master",
|
||||||
docsRepo = repo
|
docsRepo = repo
|
||||||
} = this.$site.themeConfig;
|
} = this.$site.themeConfig;
|
||||||
|
|
||||||
let path = normalize(this.$page.path);
|
let path = normalize(this.$page.path);
|
||||||
if (endingSlashRE.test(path)) {
|
if (endingSlashRE.test(path)) {
|
||||||
path += 'README.md';
|
path += "README.md";
|
||||||
} else {
|
} else {
|
||||||
path += '.md';
|
path += ".md";
|
||||||
}
|
}
|
||||||
if (docsRepo && editLinks) {
|
if (docsRepo && editLinks) {
|
||||||
return this.createEditLink(repo, docsRepo, docsDir, docsBranch, path);
|
return this.createEditLink(repo, docsRepo, docsDir, docsBranch, path);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
editLinkText() {
|
editLinkText() {
|
||||||
return (
|
return (
|
||||||
this.$themeLocaleConfig.editLinkText ||
|
this.$themeLocaleConfig.editLinkText ||
|
||||||
this.$site.themeConfig.editLinkText ||
|
this.$site.themeConfig.editLinkText ||
|
||||||
`Edit this page`
|
`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, '') +
|
|
||||||
`/${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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function resolvePrev(page, items) {
|
|
||||||
return find(page, items, - 1);
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
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, "") +
|
||||||
|
`/${docsBranch}` +
|
||||||
|
(docsDir ? "/" + docsDir.replace(endingSlashRE, "") : "") +
|
||||||
|
path +
|
||||||
|
`?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function resolveNext(page, items) {
|
const base = outboundRE.test(docsRepo)
|
||||||
return find(page, items, 1);
|
? docsRepo
|
||||||
}
|
: `https://github.com/${docsRepo}`;
|
||||||
|
|
||||||
function find(page, items, offset) {
|
return (
|
||||||
const res = [];
|
base.replace(endingSlashRE, "") +
|
||||||
items.forEach(item => {
|
`/edit/${docsBranch}` +
|
||||||
if (item.type === 'group') {
|
(docsDir ? "/" + docsDir.replace(endingSlashRE, "") : "") +
|
||||||
res.push(...item.children || []);
|
path
|
||||||
} else {
|
);
|
||||||
res.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
for (let i = 0; i < res.length; i ++) {
|
|
||||||
const cur = res[i];
|
|
||||||
if (cur.type === 'page' && cur.path === page.path) {
|
|
||||||
return res[i + offset];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function resolvePrev(page, items) {
|
||||||
|
return find(page, items, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveNext(page, items) {
|
||||||
|
return find(page, items, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function find(page, items, offset) {
|
||||||
|
const res = [];
|
||||||
|
items.forEach(item => {
|
||||||
|
if (item.type === "group") {
|
||||||
|
res.push(...(item.children || []));
|
||||||
|
} else {
|
||||||
|
res.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (let i = 0; i < res.length; i++) {
|
||||||
|
const cur = res[i];
|
||||||
|
if (cur.type === "page" && cur.path === page.path) {
|
||||||
|
return res[i + offset];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,137 +1,137 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="search-box">
|
<div class="search-box">
|
||||||
<input
|
<input
|
||||||
@input="query = $event.target.value"
|
@input="query = $event.target.value"
|
||||||
aria-label="Search"
|
aria-label="Search"
|
||||||
:value="query"
|
:value="query"
|
||||||
placeholder="Search"
|
placeholder="Search"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
@focus="focused = true"
|
@focus="focused = true"
|
||||||
@blur="focused = false"
|
@blur="focused = false"
|
||||||
@keyup.enter="go(focusIndex)"
|
@keyup.enter="go(focusIndex)"
|
||||||
@keyup.up="onUp"
|
@keyup.up="onUp"
|
||||||
@keyup.down="onDown">
|
@keyup.down="onDown"
|
||||||
<div class="suggestion-container" v-if="showSuggestions" @mouseleave="unfocus">
|
/>
|
||||||
<div class="suggestion-padding"></div>
|
<div class="suggestion-container" v-if="showSuggestions" @mouseleave="unfocus">
|
||||||
<ul class="suggestions" :class="{ 'align-right': alignRight }">
|
<div class="suggestion-padding"></div>
|
||||||
<li class="suggestion" v-for="(s, i) in suggestions"
|
<ul class="suggestions" :class="{ 'align-right': alignRight }">
|
||||||
:class="{ focused: i === focusIndex }"
|
<li
|
||||||
@mousedown="go(i)"
|
class="suggestion"
|
||||||
@mouseenter="focus(i)">
|
v-for="(s, i) in suggestions"
|
||||||
<a :href="s.path" @click.prevent>
|
:class="{ focused: i === focusIndex }"
|
||||||
<span class="page-title">{{ s.title || s.path }}</span>
|
@mousedown="go(i)"
|
||||||
<span v-if="s.header" class="header">> {{ s.header.title }}</span>
|
@mouseenter="focus(i)"
|
||||||
</a>
|
>
|
||||||
</li>
|
<a :href="s.path" @click.prevent>
|
||||||
</ul>
|
<span class="page-title">{{ s.title || s.path }}</span>
|
||||||
</div>
|
<span v-if="s.header" class="header">> {{ s.header.title }}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
query: '',
|
query: "",
|
||||||
focused: false,
|
focused: false,
|
||||||
focusIndex: 0
|
focusIndex: 0
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
showSuggestions() {
|
|
||||||
return (
|
|
||||||
this.focused &&
|
|
||||||
this.suggestions &&
|
|
||||||
this.suggestions.length
|
|
||||||
);
|
|
||||||
},
|
|
||||||
suggestions() {
|
|
||||||
const query = this.query.trim().toLowerCase();
|
|
||||||
if (! query) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { pages, themeConfig } = this.$site;
|
|
||||||
const max = themeConfig.searchMaxSuggestions || 5;
|
|
||||||
const localePath = this.$localePath;
|
|
||||||
const matches = item => (
|
|
||||||
item.title &&
|
|
||||||
item.title.toLowerCase().indexOf(query) > - 1
|
|
||||||
);
|
|
||||||
const res = [];
|
|
||||||
for (let i = 0; i < pages.length; i ++) {
|
|
||||||
if (res.length >= max) break;
|
|
||||||
const p = pages[i];
|
|
||||||
// filter out results that do not match current locale
|
|
||||||
if (this.getPageLocalePath(p) !== localePath) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (matches(p)) {
|
|
||||||
res.push(p);
|
|
||||||
} else if (p.headers) {
|
|
||||||
for (let j = 0; j < p.headers.length; j ++) {
|
|
||||||
if (res.length >= max) break;
|
|
||||||
const h = p.headers[j];
|
|
||||||
if (matches(h)) {
|
|
||||||
res.push(Object.assign({}, p, {
|
|
||||||
path: p.path + '#' + h.slug,
|
|
||||||
header: h
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
},
|
|
||||||
// make suggestions align right when there are not enough items
|
|
||||||
alignRight() {
|
|
||||||
const navCount = (this.$site.themeConfig.nav || []).length;
|
|
||||||
const repo = this.$site.repo ? 1 : 0;
|
|
||||||
return navCount + repo <= 2;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getPageLocalePath(page) {
|
|
||||||
for (const localePath in this.$site.locales || {}) {
|
|
||||||
if (localePath !== '/' && page.path.indexOf(localePath) === 0) {
|
|
||||||
return localePath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '/';
|
|
||||||
},
|
|
||||||
onUp() {
|
|
||||||
if (this.showSuggestions) {
|
|
||||||
if (this.focusIndex > 0) {
|
|
||||||
this.focusIndex --;
|
|
||||||
} else {
|
|
||||||
this.focusIndex = this.suggestions.length - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onDown() {
|
|
||||||
if (this.showSuggestions) {
|
|
||||||
if (this.focusIndex < this.suggestions.length - 1) {
|
|
||||||
this.focusIndex ++;
|
|
||||||
} else {
|
|
||||||
this.focusIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
go(i) {
|
|
||||||
if (! this.showSuggestions) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.$router.push(this.suggestions[i].path);
|
|
||||||
this.query = '';
|
|
||||||
this.focusIndex = 0;
|
|
||||||
},
|
|
||||||
focus(i) {
|
|
||||||
this.focusIndex = i;
|
|
||||||
},
|
|
||||||
unfocus() {
|
|
||||||
this.focusIndex = - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
showSuggestions() {
|
||||||
|
return this.focused && this.suggestions && this.suggestions.length;
|
||||||
|
},
|
||||||
|
suggestions() {
|
||||||
|
const query = this.query.trim().toLowerCase();
|
||||||
|
if (!query) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { pages, themeConfig } = this.$site;
|
||||||
|
const max = themeConfig.searchMaxSuggestions || 5;
|
||||||
|
const localePath = this.$localePath;
|
||||||
|
const matches = item =>
|
||||||
|
item.title && item.title.toLowerCase().indexOf(query) > -1;
|
||||||
|
const res = [];
|
||||||
|
for (let i = 0; i < pages.length; i++) {
|
||||||
|
if (res.length >= max) break;
|
||||||
|
const p = pages[i];
|
||||||
|
// filter out results that do not match current locale
|
||||||
|
if (this.getPageLocalePath(p) !== localePath) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (matches(p)) {
|
||||||
|
res.push(p);
|
||||||
|
} else if (p.headers) {
|
||||||
|
for (let j = 0; j < p.headers.length; j++) {
|
||||||
|
if (res.length >= max) break;
|
||||||
|
const h = p.headers[j];
|
||||||
|
if (matches(h)) {
|
||||||
|
res.push(
|
||||||
|
Object.assign({}, p, {
|
||||||
|
path: p.path + "#" + h.slug,
|
||||||
|
header: h
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
// make suggestions align right when there are not enough items
|
||||||
|
alignRight() {
|
||||||
|
const navCount = (this.$site.themeConfig.nav || []).length;
|
||||||
|
const repo = this.$site.repo ? 1 : 0;
|
||||||
|
return navCount + repo <= 2;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getPageLocalePath(page) {
|
||||||
|
for (const localePath in this.$site.locales || {}) {
|
||||||
|
if (localePath !== "/" && page.path.indexOf(localePath) === 0) {
|
||||||
|
return localePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "/";
|
||||||
|
},
|
||||||
|
onUp() {
|
||||||
|
if (this.showSuggestions) {
|
||||||
|
if (this.focusIndex > 0) {
|
||||||
|
this.focusIndex--;
|
||||||
|
} else {
|
||||||
|
this.focusIndex = this.suggestions.length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDown() {
|
||||||
|
if (this.showSuggestions) {
|
||||||
|
if (this.focusIndex < this.suggestions.length - 1) {
|
||||||
|
this.focusIndex++;
|
||||||
|
} else {
|
||||||
|
this.focusIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
go(i) {
|
||||||
|
if (!this.showSuggestions) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.$router.push(this.suggestions[i].path);
|
||||||
|
this.query = "";
|
||||||
|
this.focusIndex = 0;
|
||||||
|
},
|
||||||
|
focus(i) {
|
||||||
|
this.focusIndex = i;
|
||||||
|
},
|
||||||
|
unfocus() {
|
||||||
|
this.focusIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,70 +1,72 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<NavLinks class="block md:hidden"/>
|
<NavLinks class="block md:hidden" />
|
||||||
<slot name="top"/>
|
<slot name="top" />
|
||||||
<ul class="sidebar-links" v-if="items.length">
|
<ul class="sidebar-links" v-if="items.length">
|
||||||
<li v-for="(item, i) in items" :key="i">
|
<li v-for="(item, i) in items" :key="item.path">
|
||||||
<SidebarGroup v-if="item.type === 'group'"
|
<SidebarGroup
|
||||||
:item="item"
|
v-if="item.type === 'group'"
|
||||||
:first="i === 0"
|
:item="item"
|
||||||
:open="i === openGroupIndex"
|
:first="i === 0"
|
||||||
:collapsable="item.collapsable"
|
:open="i === openGroupIndex"
|
||||||
@toggle="toggleGroup(i)"/>
|
:collapsable="item.collapsable"
|
||||||
<SidebarLink v-else :item="item"/>
|
@toggle="toggleGroup(i)"
|
||||||
</li>
|
/>
|
||||||
</ul>
|
<SidebarLink v-else :item="item" />
|
||||||
<slot name="bottom"/>
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
|
<slot name="bottom" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SidebarGroup from './SidebarGroup.vue';
|
import SidebarGroup from "./SidebarGroup.vue";
|
||||||
import SidebarLink from './SidebarLink.vue';
|
import SidebarLink from "./SidebarLink.vue";
|
||||||
import NavLinks from './NavLinks.vue';
|
import NavLinks from "./NavLinks.vue";
|
||||||
import { isActive } from './util';
|
import { isActive } from "./util";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { SidebarGroup, SidebarLink, NavLinks },
|
components: { SidebarGroup, SidebarLink, NavLinks },
|
||||||
props: ['items'],
|
props: ["items"],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
openGroupIndex: 0
|
openGroupIndex: 0
|
||||||
};
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.refreshIndex();
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$route'() {
|
|
||||||
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.path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
},
|
||||||
function resolveOpenGroupIndex(route, items) {
|
created() {
|
||||||
for (let i = 0; i < items.length; i ++) {
|
this.refreshIndex();
|
||||||
const item = items[i];
|
},
|
||||||
if (item.type === 'group' && item.children.some(c => isActive(route, c.path))) {
|
watch: {
|
||||||
return i;
|
$route() {
|
||||||
}
|
this.refreshIndex();
|
||||||
}
|
|
||||||
return - 1;
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
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.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function resolveOpenGroupIndex(route, items) {
|
||||||
|
for (let i = 0; i < items.length; i++) {
|
||||||
|
const item = items[i];
|
||||||
|
if (
|
||||||
|
item.type === "group" &&
|
||||||
|
item.children.some(c => isActive(route, c.path))
|
||||||
|
) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,28 +1,90 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sidebar-group" :class="{ first, collapsable }">
|
<div class="sidebar-group" :class="{ first, collapsable }">
|
||||||
<p class="sidebar-heading" :class="{ open }" @click="$emit('toggle')">
|
<p class="sidebar-heading" :class="{ open }" @click="$emit('toggle')">
|
||||||
<span>{{ item.title }}</span>
|
<span>{{ item.title }}</span>
|
||||||
<span class="arrow"
|
<span class="arrow" v-if="collapsable" :class="open ? 'down' : 'right'"></span>
|
||||||
v-if="collapsable"
|
<VersionSelect
|
||||||
:class="open ? 'down' : 'right'"></span>
|
class="float-right"
|
||||||
</p>
|
v-if="isVersioned"
|
||||||
<DropdownTransition>
|
:versions="item.versions"
|
||||||
<ul class="sidebar-group-items" ref="items" v-if="open || !collapsable">
|
v-model="versionSelect"
|
||||||
<li v-for="child in item.children">
|
/>
|
||||||
<SidebarLink :item="child"/>
|
</p>
|
||||||
</li>
|
<DropdownTransition>
|
||||||
</ul>
|
<ul class="sidebar-group-items" ref="items" v-if="open || !collapsable">
|
||||||
</DropdownTransition>
|
<li v-for="child in children">
|
||||||
</div>
|
<SidebarLink :item="child" />
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</DropdownTransition>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import SidebarLink from './SidebarLink.vue';
|
import SidebarLink from "./SidebarLink.vue";
|
||||||
import DropdownTransition from './DropdownTransition.vue';
|
import DropdownTransition from "./DropdownTransition.vue";
|
||||||
|
import VersionSelect from "./VersionSelect.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'SidebarGroup',
|
name: "SidebarGroup",
|
||||||
props: ['item', 'first', 'open', 'collapsable'],
|
props: ["item", "first", "open", "collapsable"],
|
||||||
components: { SidebarLink, DropdownTransition }
|
components: { SidebarLink, DropdownTransition, VersionSelect },
|
||||||
|
data: function() {
|
||||||
|
let isVersioned = this.item.versions.length > 0;
|
||||||
|
|
||||||
|
let versionSelect = "";
|
||||||
|
if (isVersioned) {
|
||||||
|
versionSelect = this.item.currentVersion || this.item.versions[0].name;
|
||||||
|
if (this.$router.currentRoute.path.startsWith(this.item.path)) {
|
||||||
|
const pathVersion = this.$router.currentRoute.path.split("/")[2];
|
||||||
|
versionSelect = ~this.item.versions
|
||||||
|
.map(v => v.name)
|
||||||
|
.indexOf(pathVersion)
|
||||||
|
? pathVersion
|
||||||
|
: this.item.currentVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isVersioned,
|
||||||
|
versionSelect
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
versionSelect(newVersion, oldVersion) {
|
||||||
|
if (
|
||||||
|
oldVersion !== newVersion &&
|
||||||
|
this.$router.currentRoute.path.startsWith(this.item.path) &&
|
||||||
|
this.selectedVersion.children.length > 0
|
||||||
|
) {
|
||||||
|
// Try to navigate to the same page in the new version, or default to the first one
|
||||||
|
let path = this.$router.currentRoute.path;
|
||||||
|
path = path.substr(path.indexOf(oldVersion) + oldVersion.length);
|
||||||
|
this.$router.push(
|
||||||
|
this.selectedVersion.children.find(c => {
|
||||||
|
return c.path.endsWith(path);
|
||||||
|
}) || this.selectedVersion.children[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
$route(to, from) {
|
||||||
|
if (this.isVersioned && to.path.startsWith(this.item.path)) {
|
||||||
|
const pathVersion = to.path.split("/")[2];
|
||||||
|
if (~this.item.versions.map(v => v.name).indexOf(pathVersion)) {
|
||||||
|
this.versionSelect = pathVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectedVersion: function() {
|
||||||
|
return this.item.versions.find(v => v.name === this.versionSelect);
|
||||||
|
},
|
||||||
|
children: function() {
|
||||||
|
return this.isVersioned
|
||||||
|
? this.selectedVersion.children
|
||||||
|
: this.item.children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
63
.vuepress/theme/VersionSelect.vue
Normal file
63
.vuepress/theme/VersionSelect.vue
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<div class="version-select custom-select" :tabindex="tabindex" @blur="open = false">
|
||||||
|
<div class="selected" :class="{open: open}" @click="open = !open">
|
||||||
|
<VersionSelectItem :version="selected" />
|
||||||
|
<span class="arrow"></span>
|
||||||
|
</div>
|
||||||
|
<div class="items" :class="{hidden: !open}">
|
||||||
|
<div
|
||||||
|
class="item"
|
||||||
|
v-for="version in versions"
|
||||||
|
:key="version.name"
|
||||||
|
@click="selected=version; open=false; $emit('input', version.name)"
|
||||||
|
>
|
||||||
|
<VersionSelectItem :version="version" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import VersionSelectItem from "./VersionSelectItem.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "VersionSelect",
|
||||||
|
components: { VersionSelectItem },
|
||||||
|
props: {
|
||||||
|
versions: {
|
||||||
|
type: Array,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
tabindex: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: String,
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
selected:
|
||||||
|
this.versions.find(v => v.name === this.value) ||
|
||||||
|
(this.versions.length > 0 ? this.versions[0] : null),
|
||||||
|
open: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value(newValue, oldValue) {
|
||||||
|
if (newValue !== oldValue) {
|
||||||
|
let version = this.versions.find(v => v.name === this.value);
|
||||||
|
if (version) {
|
||||||
|
this.selected = version;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.$emit("input", this.selected.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
32
.vuepress/theme/VersionSelectItem.vue
Normal file
32
.vuepress/theme/VersionSelectItem.vue
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<template>
|
||||||
|
<div class="inline-block">
|
||||||
|
{{ version.name }}
|
||||||
|
<span class="rounded-full ml-2" :class="classes">{{version.status}}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import VersionSelectItem from "./VersionSelectItem.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "VersionSelectItem",
|
||||||
|
props: {
|
||||||
|
version: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
classes() {
|
||||||
|
return (
|
||||||
|
{
|
||||||
|
deprecated: ["text-orange"],
|
||||||
|
current: ["text-green-dark"],
|
||||||
|
stable: ["text-green-dark"],
|
||||||
|
beta: ["text-blue"]
|
||||||
|
}[this.version.status] || ["text-grey"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -1,6 +1,6 @@
|
|||||||
.content {
|
.content {
|
||||||
code {
|
code {
|
||||||
@apply .m-0 .text-sm .bg-grey-lighter .rounded .border;
|
@apply .m-0 .text-sm .border .border-grey-light .bg-grey-lighter .rounded;
|
||||||
padding: .15rem .4rem;
|
padding: .15rem .4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ div[class*="language-"] {
|
|||||||
@apply .relative .bg-grey-darkest .rounded .border .border-black .text-sm .my-4;
|
@apply .relative .bg-grey-darkest .rounded .border .border-black .text-sm .my-4;
|
||||||
|
|
||||||
.highlight-lines {
|
.highlight-lines {
|
||||||
@apply .absolute .pin-l .w-full .leading-normal .select-none;
|
@apply .absolute .left-0 .w-full .leading-normal .select-none;
|
||||||
top: .9rem;
|
top: .9rem;
|
||||||
|
|
||||||
.highlighted {
|
.highlighted {
|
||||||
@ -57,7 +57,7 @@ div[class*="language-"] {
|
|||||||
@apply .relative;
|
@apply .relative;
|
||||||
|
|
||||||
&:before {
|
&:before {
|
||||||
@apply .absolute .pin-l .pin-t .block .h-full .w-4;
|
@apply .absolute .left-0 .top-0 .block .h-full .w-4;
|
||||||
content: ' ';
|
content: ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +67,7 @@ div[class*="language-"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.line-numbers-wrapper {
|
.line-numbers-wrapper {
|
||||||
@apply .absolute .pin-t .text-center .w-4;
|
@apply .absolute .top-0 .text-center .w-4;
|
||||||
|
|
||||||
br {
|
br {
|
||||||
@apply .select-none;
|
@apply .select-none;
|
||||||
@ -78,7 +78,7 @@ div[class*="language-"] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
&::after {
|
&::after {
|
||||||
@apply .absolute .pin-t .pin-l .h-full .w-4;
|
@apply .absolute .top-0 .left-0 .h-full .w-4;
|
||||||
content: '';
|
content: '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'et-line';
|
font-family: 'et-line';
|
||||||
src:url('../fonts/et-line/et-line.eot');
|
src:url('./fonts/et-line/et-line.eot');
|
||||||
src:url('../fonts/et-line/et-line.eot?#iefix') format('embedded-opentype'),
|
src:url('./fonts/et-line/et-line.eot?#iefix') format('embedded-opentype'),
|
||||||
url('../fonts/et-line/et-line.woff') format('woff'),
|
url('./fonts/et-line/et-line.woff') format('woff'),
|
||||||
url('../fonts/et-line/et-line.ttf') format('truetype'),
|
url('./fonts/et-line/et-line.ttf') format('truetype'),
|
||||||
url('../fonts/et-line/et-line.svg#et-line') format('svg');
|
url('./fonts/et-line/et-line.svg#et-line') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
@import "tailwindcss/preflight";
|
@import "tailwindcss/base";
|
||||||
@import "tailwindcss/components";
|
@import "tailwindcss/components";
|
||||||
|
|
||||||
@import "./layout.css";
|
@import "./layout.css";
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
@apply .px-6;
|
@apply .px-6;
|
||||||
|
|
||||||
.logo-container {
|
.logo-container {
|
||||||
@apply .flex-no-shrink;
|
@apply .flex-shrink-0;
|
||||||
width: 18.5rem; /* sidebar is 20rem + 1.5rem padding on each side for nav */
|
width: 18.5rem; /* sidebar is 20rem + 1.5rem padding on each side for nav */
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
input {
|
input {
|
||||||
transition: all 200ms ease-in;
|
transition: all 200ms ease-in;
|
||||||
@apply .text-grey-lighter .w-1/2 .outline-none .px-4 .py-2 .rounded;
|
@apply .text-grey-lighter .w-1/2 .outline-none .px-4 .py-2 .rounded .leading-tight;
|
||||||
background: rgba(0, 0, 0, 0.2);
|
background: rgba(0, 0, 0, 0.2);
|
||||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||||
|
|
||||||
|
|||||||
@ -5,13 +5,13 @@ $arrow-bg: #000;
|
|||||||
@apply .hidden;
|
@apply .hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@apply .fixed .pin-l .pin-b .bg-white .overflow-auto .border-r .border-grey-lighter .py-8;
|
@apply .fixed .left-0 .bottom-0 .bg-white .overflow-auto .border-r .border-grey-lighter .py-8;
|
||||||
top: 56px;
|
top: 56px;
|
||||||
width: 20rem;
|
width: 20rem;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
@apply .list-reset .m-0;
|
@apply .m-0 .p-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
@ -90,6 +90,56 @@ $arrow-bg: #000;
|
|||||||
&.open .arrow {
|
&.open .arrow {
|
||||||
top: -0.18em;
|
top: -0.18em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.version-select {
|
||||||
|
@apply .relative .inline-block .text-sm;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
@apply .outline-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
@apply .select-none .border .border-grey-lighter .cursor-pointer .px-4;
|
||||||
|
border-radius: 1rem;
|
||||||
|
|
||||||
|
&:hover, &.open {
|
||||||
|
@apply .bg-grey-lighter .border-grey-lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow {
|
||||||
|
@apply .inline-block .relative;
|
||||||
|
top: -0.16em;
|
||||||
|
left: .5em;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
border-right: 4px solid transparent;
|
||||||
|
border-top: 6px solid #b8c2cc;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.items {
|
||||||
|
@apply .absolute .right-0 .bg-white .shadow .p-1 .mt-1;
|
||||||
|
border-radius: 1rem;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
@apply .select-none .rounded-full .cursor-pointer .px-2 .border .border-white;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin-top: .125rem;
|
||||||
|
margin-bottom: .125rem;
|
||||||
|
|
||||||
|
&:hover, &:focus {
|
||||||
|
@apply .bg-grey-lighter;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
@apply .mb-0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-group-items {
|
.sidebar-group-items {
|
||||||
@ -125,7 +175,7 @@ $arrow-bg: #000;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-mask {
|
.sidebar-mask {
|
||||||
@apply .fixed .pin-t .pin-l .h-screen .w-screen .hidden;
|
@apply .fixed .top-0 .left-0 .h-screen .w-screen .hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-open > .sidebar {
|
.sidebar-open > .sidebar {
|
||||||
|
|||||||
@ -118,7 +118,7 @@
|
|||||||
@charset 'UTF-8';
|
@charset 'UTF-8';
|
||||||
/* Slider */
|
/* Slider */
|
||||||
.slick-loading .slick-list {
|
.slick-loading .slick-list {
|
||||||
background: #fff url('../../public/frontpage/loading.svg') center center no-repeat;
|
background: #fff url('../public/frontpage/loading.svg') center center no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Icons */
|
/* Icons */
|
||||||
@ -127,8 +127,8 @@
|
|||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
|
||||||
src: url('../fonts/slick/slick.eot');
|
src: url('./fonts/slick/slick.eot');
|
||||||
src: url('../fonts/slick/slick.eot?#iefix') format('embedded-opentype'), url('../fonts/slick/slick.woff') format('woff'), url('../fonts/slick/slick.ttf') format('truetype'), url('../fonts/slick/slick.svg#slick') format('svg');
|
src: url('./fonts/slick/slick.eot?#iefix') format('embedded-opentype'), url('./fonts/slick/slick.woff') format('woff'), url('./fonts/slick/slick.ttf') format('truetype'), url('./fonts/slick/slick.svg#slick') format('svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Arrows */
|
/* Arrows */
|
||||||
|
|||||||
@ -14,12 +14,20 @@ a {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
kbd {
|
.content {
|
||||||
@apply .border .border-grey-lighter .bg-grey-lightest;
|
ul, ol {
|
||||||
}
|
@apply .ml-6 .pl-8 .my-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
padding-left: theme('padding.4');
|
||||||
|
}
|
||||||
|
|
||||||
ul, ol {
|
ol {
|
||||||
@apply .pl-8 .my-2;
|
list-style-type: decimal;
|
||||||
|
padding-left: theme('padding.4');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
@ -65,15 +73,12 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
@apply .pb-2 .border-b;
|
@apply .text-2xl .pb-2 .border-b .border-grey-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
@apply .my-4;
|
@apply .my-4;
|
||||||
}
|
line-height: 1.7;
|
||||||
|
|
||||||
code, kbd, .line-number {
|
|
||||||
@apply font-mono;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p, ul, ol {
|
p, ul, ol {
|
||||||
@ -97,7 +102,7 @@ table {
|
|||||||
}
|
}
|
||||||
|
|
||||||
th, td {
|
th, td {
|
||||||
@apply .border .py-2 .px-4;
|
@apply .border .py-2 .px-4 .border-grey-light;
|
||||||
}
|
}
|
||||||
|
|
||||||
td {
|
td {
|
||||||
|
|||||||
@ -3,32 +3,32 @@ export const extRE = /\.(md|html)$/
|
|||||||
export const endingSlashRE = /\/$/
|
export const endingSlashRE = /\/$/
|
||||||
export const outboundRE = /^(https?:|mailto:|tel:)/
|
export const outboundRE = /^(https?:|mailto:|tel:)/
|
||||||
|
|
||||||
export function normalize (path) {
|
export function normalize(path) {
|
||||||
return decodeURI(path)
|
return decodeURI(path)
|
||||||
.replace(hashRE, '')
|
.replace(hashRE, '')
|
||||||
.replace(extRE, '')
|
.replace(extRE, '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHash (path) {
|
export function getHash(path) {
|
||||||
const match = path.match(hashRE)
|
const match = path.match(hashRE)
|
||||||
if (match) {
|
if (match) {
|
||||||
return match[0]
|
return match[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isExternal (path) {
|
export function isExternal(path) {
|
||||||
return outboundRE.test(path)
|
return outboundRE.test(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isMailto (path) {
|
export function isMailto(path) {
|
||||||
return /^mailto:/.test(path)
|
return /^mailto:/.test(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isTel (path) {
|
export function isTel(path) {
|
||||||
return /^tel:/.test(path)
|
return /^tel:/.test(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ensureExt (path) {
|
export function ensureExt(path) {
|
||||||
if (isExternal(path)) {
|
if (isExternal(path)) {
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ export function ensureExt (path) {
|
|||||||
return normalized + '.html' + hash
|
return normalized + '.html' + hash
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isActive (route, path) {
|
export function isActive(route, path) {
|
||||||
const routeHash = route.hash
|
const routeHash = route.hash
|
||||||
const linkHash = getHash(path)
|
const linkHash = getHash(path)
|
||||||
if (linkHash && routeHash !== linkHash) {
|
if (linkHash && routeHash !== linkHash) {
|
||||||
@ -53,7 +53,7 @@ export function isActive (route, path) {
|
|||||||
return routePath === pagePath
|
return routePath === pagePath
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolvePage (pages, rawPath, base) {
|
export function resolvePage(pages, rawPath, base) {
|
||||||
if (base) {
|
if (base) {
|
||||||
rawPath = resolvePath(rawPath, base)
|
rawPath = resolvePath(rawPath, base)
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ export function resolvePage (pages, rawPath, base) {
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolvePath (relative, base, append) {
|
function resolvePath(relative, base, append) {
|
||||||
const firstChar = relative.charAt(0)
|
const firstChar = relative.charAt(0)
|
||||||
if (firstChar === '/') {
|
if (firstChar === '/') {
|
||||||
return relative
|
return relative
|
||||||
@ -108,7 +108,7 @@ function resolvePath (relative, base, append) {
|
|||||||
return stack.join('/')
|
return stack.join('/')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveSidebarItems (page, route, site, localePath) {
|
export function resolveSidebarItems(page, route, site, localePath) {
|
||||||
const { pages, themeConfig } = site
|
const { pages, themeConfig } = site
|
||||||
|
|
||||||
const localeConfig = localePath && themeConfig.locales
|
const localeConfig = localePath && themeConfig.locales
|
||||||
@ -131,7 +131,7 @@ export function resolveSidebarItems (page, route, site, localePath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveHeaders (page) {
|
function resolveHeaders(page) {
|
||||||
const headers = groupHeaders(page.headers || [])
|
const headers = groupHeaders(page.headers || [])
|
||||||
return [{
|
return [{
|
||||||
type: 'group',
|
type: 'group',
|
||||||
@ -147,7 +147,7 @@ function resolveHeaders (page) {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
export function groupHeaders (headers) {
|
export function groupHeaders(headers) {
|
||||||
// group h3s under h2
|
// group h3s under h2
|
||||||
headers = headers.map(h => Object.assign({}, h))
|
headers = headers.map(h => Object.assign({}, h))
|
||||||
let lastH2
|
let lastH2
|
||||||
@ -161,13 +161,13 @@ export function groupHeaders (headers) {
|
|||||||
return headers.filter(h => h.level === 2)
|
return headers.filter(h => h.level === 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveNavLinkItem (linkItem) {
|
export function resolveNavLinkItem(linkItem) {
|
||||||
return Object.assign(linkItem, {
|
return Object.assign(linkItem, {
|
||||||
type: linkItem.items && linkItem.items.length ? 'links' : 'link'
|
type: linkItem.items && linkItem.items.length ? 'links' : 'link'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveMatchingConfig (route, config) {
|
export function resolveMatchingConfig(route, config) {
|
||||||
if (Array.isArray(config)) {
|
if (Array.isArray(config)) {
|
||||||
return {
|
return {
|
||||||
base: '/',
|
base: '/',
|
||||||
@ -185,13 +185,13 @@ export function resolveMatchingConfig (route, config) {
|
|||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureEndingSlash (path) {
|
function ensureEndingSlash(path) {
|
||||||
return /(\.html|\/)$/.test(path)
|
return /(\.html|\/)$/.test(path)
|
||||||
? path
|
? path
|
||||||
: path + '/'
|
: path + '/'
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveItem (item, pages, base, isNested) {
|
function resolveItem(item, pages, base, isNested) {
|
||||||
if (typeof item === 'string') {
|
if (typeof item === 'string') {
|
||||||
return resolvePage(pages, item, base)
|
return resolvePage(pages, item, base)
|
||||||
} else if (Array.isArray(item)) {
|
} else if (Array.isArray(item)) {
|
||||||
@ -206,11 +206,17 @@ function resolveItem (item, pages, base, isNested) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
const children = item.children || []
|
const children = item.children || []
|
||||||
|
const versions = item.versions || []
|
||||||
return {
|
return {
|
||||||
type: 'group',
|
type: 'group',
|
||||||
title: item.title,
|
...item,
|
||||||
children: children.map(child => resolveItem(child, pages, base, true)),
|
children: children.map(child => resolveItem(child, pages, base, true)),
|
||||||
collapsable: item.collapsable !== false
|
collapsable: item.collapsable !== false,
|
||||||
|
versions: versions.map(version => ({
|
||||||
|
...version,
|
||||||
|
status: version.name === item.currentVersion ? "current" : version.status,
|
||||||
|
children: version.children.map(child => resolveItem(item.path + version.name + child, pages, base, true))
|
||||||
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ Give theme name:
|
|||||||
> (Press Enter)
|
> (Press Enter)
|
||||||
|
|
||||||
Where will assets be located? []:
|
Where will assets be located? []:
|
||||||
> /themes/(MyThemeNameHere) (Example: /themes/MyThemeNameHere)
|
> themes/(MyThemeNameHere) (Example: themes/MyThemeNameHere)
|
||||||
|
|
||||||
Extends an other theme? (yes/no) [no]:
|
Extends an other theme? (yes/no) [no]:
|
||||||
> (Press Enter)
|
> (Press Enter)
|
||||||
|
|||||||
@ -148,3 +148,9 @@ Starting with `v0.6` of the Daemon, the following previously _dropped_ capabilit
|
|||||||
'setfcap',
|
'setfcap',
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Enabling Cloudflare
|
||||||
|
|
||||||
|
Enabling Cloudflare on the daemon isn't particularly useful since users do not connect directly to the daemon port, and users need an unproxied hostname to access any servers on the node. As a result it's not possible to conceal the IP address of your node machine, but some people want to enable it regardless.
|
||||||
|
|
||||||
|
Cloudflare only proxies the default daemon port (8080) when using HTTP. In order to get the daemon to work with Cloudflare when HTTPS is enabled you must change the daemon port to one that Cloudflare will proxy such as 8443. Since Cloudflare only proxies HTTP/HTTPS traffic for non-enterprise plans you cannot proxy the SFTP port.
|
||||||
@ -5,17 +5,12 @@
|
|||||||
## Supported Systems
|
## Supported Systems
|
||||||
| Operating System | Version | Supported | Notes |
|
| Operating System | Version | Supported | Notes |
|
||||||
| ---------------- | ------- | :-------: | ----- |
|
| ---------------- | ------- | :-------: | ----- |
|
||||||
| **Ubuntu** | 14.04 | :warning: | Approaching EOL, not recommended for new installations. |
|
| **Ubuntu** | 18.04 | :white_check_mark: | Documentation written assuming Ubuntu 18.04 as the base OS. |
|
||||||
| | 16.04 | :white_check_mark: | |
|
| | 20.04 | :white_check_mark: |
|
||||||
| | 18.04 | :white_check_mark: | |
|
| **CentOS** | 7 | :warning: | Extra repos are required |
|
||||||
| **CentOS** | 6 | :no_entry_sign: | Does not support all of the required packages. |
|
| | 8 | :white_check_mark: | |
|
||||||
| | 7 | :white_check_mark: | |
|
| **Debian** | 9 | :white_check_mark: | |
|
||||||
| **Debian** | 8 | :warning: | Requires [kernel modifications](debian_8_docker.md) to run Docker. |
|
| | 10 | :white_check_mark: | |
|
||||||
| | 9 | :white_check_mark: | |
|
|
||||||
| **Alpine Linux** | 3.4+ | :warning: | Not officially supported, but reportedly works. |
|
|
||||||
| **RHEL** | 7 | :warning: | Not officially supported, should work. |
|
|
||||||
| **Fedora** | 28 | :warning: | Not officially supported, should work. |
|
|
||||||
| | 29 | :warning: | Not officially supported, should work. |
|
|
||||||
|
|
||||||
## System Requirements
|
## System Requirements
|
||||||
In order to run the Daemon you will need a system capable of running Docker containers. Most VPS and almost all
|
In order to run the Daemon you will need a system capable of running Docker containers. Most VPS and almost all
|
||||||
@ -148,7 +143,7 @@ Once you have done that there will be a tab called Configuration when you view t
|
|||||||
Simply copy and paste the code block and paste it into a file called `core.json` in `/srv/daemon/config` and save it.
|
Simply copy and paste the code block and paste it into a file called `core.json` in `/srv/daemon/config` and save it.
|
||||||
You may also use the Auto-Deployment feature rather than manually creating the files.
|
You may also use the Auto-Deployment feature rather than manually creating the files.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Starting the Daemon
|
## Starting the Daemon
|
||||||
To start your daemon simply move into the daemon directory and run the command below which will start the daemon in
|
To start your daemon simply move into the daemon directory and run the command below which will start the daemon in
|
||||||
@ -192,18 +187,3 @@ Then, run the commands below to reload systemd and start the daemon.
|
|||||||
``` bash
|
``` bash
|
||||||
systemctl enable --now wings
|
systemctl enable --now wings
|
||||||
```
|
```
|
||||||
|
|
||||||
### Daemonizing (using Forever)
|
|
||||||
Forever allows us to run the daemon as a pseudo-daemonized application. We can exit our terminal session without
|
|
||||||
killing the process, and we don't have to run it in a screen. Inside the daemon directory, run the commands below which
|
|
||||||
will install forever and then start the process in the background.
|
|
||||||
|
|
||||||
You should use this only if your system does not support systemd.
|
|
||||||
|
|
||||||
``` bash
|
|
||||||
npm install -g forever
|
|
||||||
forever start src/index.js
|
|
||||||
|
|
||||||
# To stop the daemon use:
|
|
||||||
forever stop src/index.js
|
|
||||||
```
|
|
||||||
14
package.json
14
package.json
@ -1,19 +1,21 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@vuepress/plugin-google-analytics": "^1.0.0-rc.1",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"slick-carousel": "^1.8.1",
|
"slick-carousel": "^1.8.1",
|
||||||
"vuepress": "^0.14.8"
|
"vuepress": "^1.4.0",
|
||||||
|
"vuepress-plugin-container": "^2.1.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "./node_modules/vuepress/bin/vuepress.js build",
|
"build": "vuepress build",
|
||||||
"watch": "./node_modules/vuepress/bin/vuepress.js dev"
|
"watch": "vuepress dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "^9.0.1",
|
"autoprefixer": "^9.0.1",
|
||||||
"cssnano": "^4.0.3",
|
"cssnano": "^4.0.3",
|
||||||
"lodash": "^4.17.13",
|
"lodash": "^4.17.13",
|
||||||
"postcss-import": "^11.1.0",
|
"postcss-import": "^12.0.0",
|
||||||
"precss": "^3.1.2",
|
"precss": "^4.0.0",
|
||||||
"tailwindcss": "^0.6.4"
|
"tailwindcss": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,14 +20,11 @@ this software on an OpenVZ based system you will — most likely — not
|
|||||||
|
|
||||||
| Operating System | Version | Supported | Notes |
|
| Operating System | Version | Supported | Notes |
|
||||||
| ---------------- | ------- | :-------: | ----- |
|
| ---------------- | ------- | :-------: | ----- |
|
||||||
| **Ubuntu** | 14.04 | :warning: | Documentation assumes changes to `systemd` introduced in `16.04` |
|
| **Ubuntu** | 18.04 | :white_check_mark: | Documentation written assuming Ubuntu 18.04 as the base OS. |
|
||||||
| | 16.04 | :warning: | Ubuntu 16.04 is nearing end of life. |
|
| | 20.04 | :white_check_mark: | |
|
||||||
| | 18.04 | :white_check_mark: | Documentation written assuming Ubuntu 18.04 as the base OS. |
|
| **CentOS** | 7 | :white_check_mark: | Extra repos are required. |
|
||||||
| **CentOS** | 6 | :no_entry_sign: | Does not support all of the required packages. |
|
|
||||||
| | 7 | :white_check_mark: | Extra repos are required. |
|
|
||||||
| | 8 | :white_check_mark: | All required packages are part of the base repos. |
|
| | 8 | :white_check_mark: | All required packages are part of the base repos. |
|
||||||
| **Debian** | 8 | :warning: | Debian 8 may need modifications to work with the latest docker and other requirements for the panel/daemon |
|
| **Debian** | 9 | :white_check_mark: | Extra repos are required. |
|
||||||
| | 9 | :white_check_mark: | Extra repos are required. |
|
|
||||||
| | 10 | :white_check_mark: | All required packages are part of the base repos. |
|
| | 10 | :white_check_mark: | All required packages are part of the base repos. |
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
[[toc]]
|
[[toc]]
|
||||||
|
|
||||||
|
::: danger Not for Production Use
|
||||||
|
**This is a beta version that should not be used in a production environment.**
|
||||||
|
Features haven't been fully tested and might be incomplete or broken. There is no supported way to upgrade. You should test this version with a separate installation.
|
||||||
|
:::
|
||||||
|
|
||||||
Pterodactyl Panel is designed to run on your own web server. You will need to have root access to your server in order to run and use this panel.
|
Pterodactyl Panel is designed to run on your own web server. You will need to have root access to your server in order to run and use this panel.
|
||||||
|
|
||||||
You are expected to understand how to read documentation to use this Panel. We have spent many hours detailing how to install or upgrade our
|
You are expected to understand how to read documentation to use this Panel. We have spent many hours detailing how to install or upgrade our
|
||||||
@ -20,13 +25,11 @@ this software on an OpenVZ based system you will — most likely — not
|
|||||||
|
|
||||||
| Operating System | Version | Supported | Notes |
|
| Operating System | Version | Supported | Notes |
|
||||||
| ---------------- | ------- | :-------: | ----- |
|
| ---------------- | ------- | :-------: | ----- |
|
||||||
| **Ubuntu** | 16.04 | :warning: | Ubuntu 16.04 is nearing end of life. |
|
| **Ubuntu** | 18.04 | :white_check_mark: | Documentation written assuming Ubuntu 18.04 as the base OS. |
|
||||||
| | 18.04 | :white_check_mark: | Documentation written assuming Ubuntu 18.04 as the base OS. |
|
| | 20.04 | :white_check_mark: | |
|
||||||
| **CentOS** | 6 | :no_entry_sign: | Does not support all of the required packages. |
|
| **CentOS** | 7 | :white_check_mark: | Extra repos are required. |
|
||||||
| | 7 | :white_check_mark: | Extra repos are required. |
|
|
||||||
| | 8 | :white_check_mark: | All required packages are part of the base repos. |
|
| | 8 | :white_check_mark: | All required packages are part of the base repos. |
|
||||||
| **Debian** | 8 | :warning: | Debian 8 may need modifications to work with the latest docker and other requirements for the panel/daemon |
|
| **Debian** | 9 | :white_check_mark: | Extra repos are required. |
|
||||||
| | 9 | :white_check_mark: | Extra repos are required. |
|
|
||||||
| | 10 | :white_check_mark: | All required packages are part of the base repos. |
|
| | 10 | :white_check_mark: | All required packages are part of the base repos. |
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|||||||
@ -17,7 +17,7 @@ members can be found in our Discord channel and are distinguished with a yellow
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
``` text
|
``` text
|
||||||
Copyright (c) 2015 - 2019 Dane Everitt <dane@daneeveritt.com>.
|
Copyright (c) 2015 - 2020 Dane Everitt <dane@daneeveritt.com>.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
|
# Community Standards
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Sorry, but this file is off limits to additions or deletions that are not the result of fixing
|
Sorry, but this file is off limits to additions or deletions that are not the result of fixing
|
||||||
grammar or spelling mistakes.
|
grammar or spelling mistakes.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# Community Standards
|
|
||||||
Pterodactyl prides itself on providing a warm and welcoming community for all of our members. However, due to the
|
Pterodactyl prides itself on providing a warm and welcoming community for all of our members. However, due to the
|
||||||
sheer size of our community, we have a few rules that we expect all individuals to follow at all times.
|
sheer size of our community, we have a few rules that we expect all individuals to follow at all times.
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,6 @@ In addition to our standard nest of supported games, our community is constantly
|
|||||||
and there are plenty more games available provided by the community. Some of these games include:
|
and there are plenty more games available provided by the community. Some of these games include:
|
||||||
|
|
||||||
* Factorio
|
* Factorio
|
||||||
* Terraria
|
|
||||||
* San Andreas: MP
|
* San Andreas: MP
|
||||||
* Pocketmine MP
|
* Pocketmine MP
|
||||||
* Squad
|
* Squad
|
||||||
|
|||||||
38
tailwind.js
38
tailwind.js
@ -123,6 +123,36 @@ let colors = {
|
|||||||
'pink-lightest': '#ffebef',
|
'pink-lightest': '#ffebef',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
prefix: '',
|
||||||
|
important: false,
|
||||||
|
separator: ':',
|
||||||
|
theme: {
|
||||||
|
colors,
|
||||||
|
screens: {
|
||||||
|
'sm': '576px',
|
||||||
|
'md': '768px',
|
||||||
|
'lg': '992px',
|
||||||
|
'xl': '1200px',
|
||||||
|
|
||||||
|
'xsx': { 'max': '575px' },
|
||||||
|
'smx': { 'max': '767px' },
|
||||||
|
'mdx': { 'max': '991px' },
|
||||||
|
'lgx': { 'max': '1999px' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
appearance: ['responsive'],
|
||||||
|
// ...
|
||||||
|
zIndex: ['responsive'],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
// ...
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -166,10 +196,10 @@ module.exports = {
|
|||||||
'lg': '992px',
|
'lg': '992px',
|
||||||
'xl': '1200px',
|
'xl': '1200px',
|
||||||
|
|
||||||
'xsx': {'max': '575px'},
|
'xsx': { 'max': '575px' },
|
||||||
'smx': {'max': '767px'},
|
'smx': { 'max': '767px' },
|
||||||
'mdx': {'max': '991px'},
|
'mdx': { 'max': '991px' },
|
||||||
'lgx': {'max': '1999px'},
|
'lgx': { 'max': '1999px' },
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -14,18 +14,12 @@ This software _requires Pterodactyl 1.0_ in order to run.
|
|||||||
## Supported Systems
|
## Supported Systems
|
||||||
| Operating System | Version | Supported | Notes |
|
| Operating System | Version | Supported | Notes |
|
||||||
| ---------------- | ------- | :-------: | ----- |
|
| ---------------- | ------- | :-------: | ----- |
|
||||||
| **Ubuntu** | 14.04 | :no_entry_sign: | Does not support `systemd`. |
|
| **Ubuntu** | 18.04 | :white_check_mark: | Documentation written assuming Ubuntu 18.04 as the base OS. |
|
||||||
| | 16.04 | :white_check_mark: | |
|
| | 20.04 | :white_check_mark: | |
|
||||||
| | 18.04 | :white_check_mark: | |
|
| **CentOS** | 7 | :white_check_mark: | |
|
||||||
| **CentOS** | 6 | :no_entry_sign: | Does not support all of the required packages. |
|
| | 8 | :white_check_mark: | |
|
||||||
| | 7 | :white_check_mark: | |
|
| **Debian** | 9 | :white_check_mark: | |
|
||||||
| **Debian** | 8 | :warning: | Requires [kernel modifications](/daemon/debian_8_docker.md) to run Docker. |
|
|
||||||
| | 9 | :white_check_mark: | |
|
|
||||||
| | 10 | :white_check_mark: | |
|
| | 10 | :white_check_mark: | |
|
||||||
| **Alpine Linux** | 3.4+ | :warning: | Not officially supported, but reportedly works. |
|
|
||||||
| **RHEL** | 7 | :warning: | Not officially supported, should work. |
|
|
||||||
| **Fedora** | 28 | :warning: | Not officially supported, should work. |
|
|
||||||
| | 29 | :warning: | Not officially supported, should work. |
|
|
||||||
|
|
||||||
## System Requirements
|
## System Requirements
|
||||||
In order to run the Daemon you will need a system capable of running Docker containers. Most VPS and almost all
|
In order to run the Daemon you will need a system capable of running Docker containers. Most VPS and almost all
|
||||||
@ -124,7 +118,7 @@ Once you have done that there will be a tab called Configuration when you view t
|
|||||||
|
|
||||||
Simply copy and paste the code block and paste it into a file called `config.yml` in `/srv/wings` and save it.
|
Simply copy and paste the code block and paste it into a file called `config.yml` in `/srv/wings` and save it.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### Starting Wings
|
### Starting Wings
|
||||||
To start your daemon simply move into the daemon directory and run the command below which will start the daemon in
|
To start your daemon simply move into the daemon directory and run the command below which will start the daemon in
|
||||||
Loading…
x
Reference in New Issue
Block a user