This commit is contained in:
Dane Everitt 2020-04-25 19:30:57 -07:00
commit 11461e29a0
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
49 changed files with 6105 additions and 4475 deletions

View File

@ -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
View 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);
}

View File

@ -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 &mdash; make game servers a first class citizen on your platform. with other systems &mdash; 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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">&gt; {{ 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">&gt; {{ 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>

View File

@ -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>

View File

@ -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>

View 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>

View 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>

View File

@ -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: '';
} }
} }

View File

@ -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;
} }

View File

@ -1,4 +1,4 @@
@import "tailwindcss/preflight"; @import "tailwindcss/base";
@import "tailwindcss/components"; @import "tailwindcss/components";
@import "./layout.css"; @import "./layout.css";

View File

@ -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);

View File

@ -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 {

View File

@ -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 */

View File

@ -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 {

View File

@ -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))
})),
} }
} }
} }

0
404.md Normal file
View File

View File

@ -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)

View File

@ -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.

View File

@ -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.
![](./../.vuepress/public/daemon_configuration_example.png) ![](./../../.vuepress/public/daemon_configuration_example.png)
## 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
```

View File

@ -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"
} }
} }

View File

@ -20,14 +20,11 @@ this software on an OpenVZ based system you will &mdash; most likely &mdash; 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

View File

@ -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 &mdash; most likely &mdash; 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

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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' },
}, },

View File

@ -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.
![](./../.vuepress/public/wings_configuration_example.png) ![](./../../.vuepress/public/wings_configuration_example.png)
### 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

8964
yarn.lock

File diff suppressed because it is too large Load Diff