(add) docs page

This commit is contained in:
kakkokari-gtyih 2023-07-12 02:19:32 +09:00
parent 2ce3522e05
commit 7aec066b8d
22 changed files with 507 additions and 49 deletions

View File

@ -0,0 +1,64 @@
<template>
<div :class="['grid grid-cols-1 md:grid-cols-2 gap-4', wide && 'lg:grid-cols-3']">
<ContentNavigation v-slot="{ navigation }" :query="query">
<GNuxtLink
class="block p-4 rounded-lg border border-slate-200 dark:border-accent-900 transition-colors hover:bg-slate-100 dark:hover:bg-slate-800 hover:!no-underline"
v-for="item in findDeepObject((navigation[0] as Record<string, any>), (v) => realBasePath.replace(/\/$/, '') === v?._path.replace(/\/$/, ''))?.children ?? []"
:key="item._path"
:to="localePath(item._path)"
>
<h3 class="font-bold text-lg mb-2">
{{ item.navTitle || item.title }}<ArrowRightIco class="ml-1.5" />
</h3>
<p class="text-sm opacity-80">
{{ item.description ?? "" }}
</p>
</GNuxtLink>
</ContentNavigation>
</div>
</template>
<script setup lang="ts">
import ArrowRightIco from "bi/arrow-right.svg";
const route = useRoute();
const { locale } = useI18n();
const props = withDefaults(defineProps<{
basePath?: string;
wide?: boolean;
}>(), {
wide: false,
});
const realBasePath = computed<string>(() => {
if (props.basePath) {
return props.basePath;
}
return route.path.replace(/^.*\/docs/, `/${locale.value}/docs`);
});
const localePath = useLocalePath();
const findDeepObject = (obj: Record<string, any>, condition: (v: any) => boolean): Record<string, any> | null => {
if (condition(obj)) {
return obj;
}
if (obj.children && obj.children.length > 0) {
for (let i = 0; i < obj.children.length; i++) {
console.log(obj.children[i]);
const result = findDeepObject(obj.children[i], condition);
if (result) {
return result;
}
}
}
return null;
};
const query = queryContent(realBasePath.value);
</script>
<style scoped></style>

View File

@ -0,0 +1,43 @@
<script setup lang="ts">
import ExternalIco from 'bi/box-arrow-up-right.svg';
const runtimeConfig = useRuntimeConfig();
const rootDomain = new URL(runtimeConfig.public.baseUrl);
const localePath = useLocalePath();
const props = defineProps({
href: {
type: String,
default: ''
},
target: {
type: String,
default: undefined,
required: false
}
})
let realHref = props.href;
let realTarget = props.target;
try {
const url = new URL(props.href);
console.log(url);
if (!url.hostname || rootDomain.hostname === url.hostname) {
realHref = localePath(realHref);
}
if (rootDomain.hostname !== url.hostname) {
realTarget = '_blank';
}
} catch(_) {
if(realHref !== '') {
realHref = localePath(realHref);
}
}
</script>
<template>
<GNuxtLink :href="realHref" :target="realTarget">
<slot></slot><ExternalIco v-if="realTarget === '_blank'" class="text-xs mx-1" />
</GNuxtLink>
</template>

View File

@ -0,0 +1,7 @@
<template>
<div class="w-full overflow-x-auto whitespace-nowrap">
<table>
<slot />
</table>
</div>
</template>

View File

@ -0,0 +1,44 @@
<script setup lang="ts">
import { upperFirst } from "scule";
const { prev, next, navigation } = useContent();
const { navDirFromPath } = useContentHelpers();
const directory = (link: any) => {
const nav = navDirFromPath(link._path, navigation.value || []);
if (nav && nav[0]) {
return nav[0]?._path ?? "";
} else {
const dirs = link.split("/");
const directory = dirs.length > 1 ? dirs[dirs.length - 2] : "";
return directory.split("-").map(upperFirst).join(" ");
}
};
</script>
<template>
<div v-if="prev || next" class="docs-prev-next">
<NuxtLink v-if="prev && prev._path" :to="prev._path" class="prev">
<Icon name="heroicons-outline:arrow-sm-left" class="icon" />
<div class="wrapper">
<span v-if="directory(prev._path)" class="directory">
{{ directory(prev._path) }}
</span>
<span class="title">{{ prev.title }}</span>
</div>
</NuxtLink>
<span v-else />
<NuxtLink v-if="next && next._path" :to="next._path" class="next">
<div class="wrapper">
<span v-if="directory(next._path)" class="directory">
{{ directory(next._path) }}
</span>
<span class="title">{{ next.title }}</span>
</div>
<Icon name="heroicons-outline:arrow-sm-right" class="icon" />
</NuxtLink>
</div>
</template>

View File

@ -1,24 +1,33 @@
<template>
<section>
<h2 class="text-2xl lg:text-3xl font-bold mb-4">{{ $t(`_docs._${sectionId}.title`) }}</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div v-for="n of 6" class="border rounded-lg p-4">
<h3 class="font-bold text-lg mb-2">大カテゴリタイトル<ArrowRightIco class="ml-1.5" /></h3>
<p class="text-sm opacity-80">カテゴリ説明文</p>
</div>
</div>
<h2 class="text-2xl lg:text-3xl font-bold mb-4">
{{ $t(`_docs._${sectionId}.title`) }}
</h2>
<MkIndex :wide="true" :base-path="basePath" />
</section>
</template>
<script setup lang="ts">
import ArrowRightIco from 'bi/arrow-right.svg';
defineProps<{
sectionId: 'forUsers' | 'forAdmin' | 'forDevelopers';
const props = defineProps<{
sectionId: "forUsers" | "forAdmin" | "forDevelopers";
}>();
const { locale } = useI18n();
const convertToKebabCase = (str: string): string => {
if (typeof str !== "string") return str;
str = str.replace(/^ *?[A-Z]/, function (allStr) {
return allStr.toLowerCase();
});
str = str.replace(/_/g, "-");
str = str.replace(/ *?[A-Z]/g, function (allStr, i) {
return "-" + allStr.replace(/\s/g, "").toLowerCase();
});
return str;
};
const basePath = `/${locale.value}/docs/${convertToKebabCase(props.sectionId)}/`;
</script>
<style scoped>
</style>
<style scoped></style>

View File

@ -0,0 +1,76 @@
<template>
<ul class="toc-links mb-4 space-y-2">
<li
v-for="link in links"
:key="link.text"
:class="[`depth-${link.depth}`]"
>
<a
:href="`#${link.id}`"
@click.prevent="scrollToHeading(link.id)"
:class="['hover:text-accent-600', activeHeadings.includes(link.id) ? 'font-bold text-accent-600' : '']"
>
{{ link.text }}
</a>
<TocLinks
class="mt-2"
v-if="link.children"
:links="link.children"
@move="childMove($event)"
/>
</li>
</ul>
</template>
<script setup lang="ts">
import type { PropType } from 'vue'
import type { TocLink } from '@nuxt/content/dist/runtime/types'
const props = defineProps({
links: {
type: Array as PropType<TocLink[]>,
default: () => []
}
});
const emit = defineEmits(['move'])
const { activeHeadings, updateHeadings } = useScrollspy();
if (process.client) {
setTimeout(() => {
updateHeadings([
...document.querySelectorAll('.markdown-body h1'),
...document.querySelectorAll('.markdown-body h2'),
...document.querySelectorAll('.markdown-body h3'),
...document.querySelectorAll('.markdown-body h4'),
]);
}, 300);
}
function scrollToHeading (id: string) {
if (process.client) {
if (!decodeURIComponent(location.href).includes(`#${id}`)) {
// history API
history.pushState({}, '', `#${id}`);
}
document.getElementById(id)?.scrollIntoView({
behavior: 'smooth'
});
}
emit('move', id)
}
function childMove(id: string) {
emit('move', id)
}
</script>
<style scoped>
.toc-links ::v-deep(.depth-3) {
@apply ml-2;
}
.toc-links ::v-deep(.depth-4) {
@apply ml-4;
}
</style>

View File

@ -1,11 +1,74 @@
<template>
<footer>
<footer class="p-4 sm:p-6 bg-white dark:bg-slate-950">
<div class="mx-auto max-w-screen-xl">
<div class="md:flex md:justify-between">
<div class="mb-6 md:mb-0">
<GNuxtLink :to="localePath('/')" class="flex items-center">
<MiIcon class="h-8 w-8 mr-3" />
<span class="self-center text-2xl font-bold font-title whitespace-nowrap">{{ $t('_seo.siteName') }}</span>
</GNuxtLink>
</div>
<div class="grid grid-cols-2 gap-8 sm:gap-6 sm:grid-cols-3">
<div>
<h2 class="mb-6 text-sm font-bold">{{ $t('_docs.title') }}</h2>
<ul class="text-slate-600 dark:text-slate-400 space-y-4">
<li>
<GNuxtLink :to="localePath('/docs/')" class="hover:underline">{{ $t('_docs.indexTitle') }}</GNuxtLink>
</li>
<li>
<GNuxtLink :to="localePath('/docs/for-users')" class="hover:underline">{{ $t('_docs._forUsers.title') }}</GNuxtLink>
</li>
<li>
<GNuxtLink :to="localePath('/docs/for-admin')" class="hover:underline">{{ $t('_docs._forAdmin.title') }}</GNuxtLink>
</li>
<li>
<GNuxtLink :to="localePath('/docs/for-developers')" class="hover:underline">{{ $t('_docs._forDevelopers.title') }}</GNuxtLink>
</li>
<li>
<GNuxtLink :to="localePath('/docs/releases')" class="hover:underline">{{ $t('_docs._changelog.title') }}</GNuxtLink>
</li>
</ul>
</div>
<div>
<h2 class="mb-6 text-sm font-bold">{{ $t('_other.title') }}</h2>
<ul class="text-slate-600 dark:text-slate-400 space-y-4">
<li>
<GNuxtLink :to="localePath('/servers/')" class="hover:underline">{{ $t('_servers.title') }}</GNuxtLink>
</li>
<li>
<GNuxtLink :to="localePath('/brand-assets/')" class="hover:underline">{{ $t('_brandAssets.title') }}</GNuxtLink>
</li>
<li>
<GNuxtLink :to="localePath('/links/')" class="hover:underline">{{ $t('_links.title') }}</GNuxtLink>
</li>
</ul>
</div>
<div>
<h2 class="mb-6 text-sm font-bold text-transparent">_</h2>
<ul class="text-slate-600 dark:text-slate-400">
<li class="mb-4">
<a href="#" class="hover:underline">Privacy Policy</a>
</li>
<li>
<a href="#" class="hover:underline">Terms &amp; Conditions</a>
</li>
</ul>
</div>
</div>
</div>
<hr class="my-6 border-slate-200 sm:mx-auto dark:border-slate-700 lg:my-8" />
<div class="sm:flex sm:items-center sm:justify-between">
<span class="text-sm text-slate-500 sm:text-center dark:text-slate-400">&copy; 2023 Misskey, syuilo, and other contributors
</span>
</div>
</div>
</footer>
</template>
<script setup lang="ts">
import MiIcon from '@/assets/svg/misskey_mi_bi.svg';
const localePath = useLocalePath();
</script>
<style scoped>

View File

@ -1,6 +1,6 @@
<template>
<div :class="['bg-slate-100 dark:bg-gray-900 bg-opacity-80 backdrop-blur-lg sticky top-0 z-[9900] transition-[box-shadow]', { 'shadow': scrollPos <= -40 }]">
<nav class="container mx-auto max-w-screen-xl h-16 lg:h-20 grid items-center grid-cols-2 md:grid-cols-4 lg:grid-cols-6 p-4">
<div :class="['bg-slate-100 dark:bg-gray-900 bg-opacity-80 backdrop-blur-lg sticky top-0 z-[9900] transition-[box-shadow]', { 'shadow': (!disableShadow && scrollPos <= -40) }]">
<nav :class="['container mx-auto max-w-screen-xl grid items-center grid-cols-2 md:grid-cols-4 lg:grid-cols-6 p-4', (slim ? 'h-16' : 'h-16 lg:h-20')]">
<div class="">
<GNuxtLink :to="localePath('/')" class="flex items-center space-x-2 hover:opacity-80">
<MiIcon class="h-8 w-8" />
@ -55,7 +55,7 @@
</div>
</template>
<script setup>
<script setup lang="ts">
import MiIcon from '@/assets/svg/misskey_mi_bi.svg';
import I18nIcon from 'bi/translate.svg';
import SunIcon from 'bi/sun.svg';
@ -63,6 +63,14 @@ import MoonIcon from 'bi/moon-stars.svg';
import DisplayIcon from 'bi/display.svg';
import NavData from '@/assets/data/nav';
const props = withDefaults(defineProps<{
disableShadow?: boolean;
slim?: boolean;
}>(), {
disableShadow: false,
slim: false,
});
const { locales, locale: currentLocale } = useI18n();
const { path } = useRoute();
const switchLocalePath = useSwitchLocalePath();
@ -83,13 +91,13 @@ async function updatePos() {
scrollPos.value = document.body.getBoundingClientRect().y;
}
if (process.client) {
if (process.client && !props.disableShadow) {
window.addEventListener('scroll', updatePos);
window.addEventListener('resize', updatePos);
}
onUnmounted(() => {
if (process.client) {
if (process.client && !props.disableShadow) {
window.removeEventListener('scroll', updatePos);
window.removeEventListener('resize', updatePos);
}

View File

@ -0,0 +1,39 @@
import type { Ref } from 'vue'
/**
* Scrollspy allows you to watch visible headings in a specific page.
* Useful for table of contents live style updates.
*/
export const useScrollspy = () => {
const observer = ref() as Ref<IntersectionObserver>
const visibleHeadings = ref([]) as Ref<string[]>
const activeHeadings = ref([]) as Ref<string[]>
const observerCallback = (entries: IntersectionObserverEntry[]) =>
entries.forEach((entry) => {
const id = entry.target.id
if (entry.isIntersecting) { visibleHeadings.value.push(id) } else { visibleHeadings.value = visibleHeadings.value.filter(t => t !== id) }
})
const updateHeadings = (headings: Element[]) =>
headings.forEach((heading) => {
observer.value.observe(heading)
})
watch(visibleHeadings, (val, oldVal) => {
if (val.length === 0) { activeHeadings.value = oldVal } else { activeHeadings.value = val }
}, { deep: true })
// Create intersection observer
onBeforeMount(() => (observer.value = new IntersectionObserver(observerCallback)))
// Destroy it
onBeforeUnmount(() => observer.value?.disconnect())
return {
visibleHeadings,
activeHeadings,
updateHeadings
}
}

View File

@ -1,8 +1,14 @@
<script setup lang="ts">
const isNavOpen = ref<boolean>(false);
useHead({
htmlAttrs: {
class: 'scroll-pt-20 lg:scroll-pt-24',
},
});
</script>
<template>
<div>
<div class="">
<GNav @toggleNav="isNavOpen = !isNavOpen" :is-open="isNavOpen" />
<div class="main-content">
<slot></slot>

18
layouts/docs.vue Normal file
View File

@ -0,0 +1,18 @@
<script setup lang="ts">
const isNavOpen = ref<boolean>(false);
useHead({
htmlAttrs: {
class: 'scroll-pt-16',
},
});
</script>
<template>
<div class="bg-white dark:bg-slate-950">
<GNav @toggleNav="isNavOpen = !isNavOpen" :is-open="isNavOpen" :slim="true" :disable-shadow="true" />
<div class="main-content">
<slot></slot>
</div>
<GFooter />
</div>
</template>

View File

@ -1,11 +1,16 @@
<script setup lang="ts">
const isNavOpen = ref<boolean>(false);
useHead({
htmlAttrs: {
class: 'scroll-pt-20 lg:scroll-pt-4',
},
});
</script>
<template>
<div>
<div class="main-content">
<slot></slot>
</div>
<GFooter />
</div>
</template>

View File

@ -105,15 +105,28 @@ _servers:
_docs:
title: "ドキュメント"
description: "Misskeyの上手なつかいかたから、サーバーの運営者・プログラムの開発者向けの情報まで網羅しています。"
indexTitle: "目次ページ"
_aboutMisskey:
title: "Misskeyについて"
description: "Misskeyをはじめて知ったかたや、これから使うかた向け基本的なしくみや機能を見ていきましょう。"
_changelog:
title: "リリースノート"
description: "Misskeyのバージョンアップ履歴をご覧いただけます。最新のバージョンで何が変わったのかを確認しましょう"
_forUsers:
title: "Misskeyユーザー向け"
_forAdmin:
title: "サーバー運営者向け"
_forDevelopers:
title: "開発者向け"
_toc:
title: "このページの内容"
toPageTop: "ページ上部に戻る"
_blog:
title: "ブログ"
description: "Misskey開発本部から、Misskeyに関する最新情報やTips等をお届けします日本語のみ"
_other:
title: "お楽しみ"
_brandAssets:
title: "アセット集"
_links:
title: "リンク"

View File

@ -2,10 +2,20 @@
import ViteYaml from '@modyfi/vite-plugin-yaml';
import svgLoader from 'vite-svg-loader';
import genSitemap from './scripts/gen-sitemap';
import { resolve } from 'path';
// 公開時のドメイン(末尾スラッシュなし)
const baseUrl = 'https://misskey-hub.net';
const locales = [
{ code: 'ja', iso: 'ja-JP', name: '日本語' },
{ code: 'en', iso: 'en-US', name: 'English' },
{ code: 'ko', iso: 'ko-KR', name: '한국어' },
{ code: 'it', iso: 'it-IT', name: 'Italiano' },
{ code: 'pl', iso: 'pl-PL', name: 'Polski' },
{ code: 'fr', iso: 'fr-FR', name: 'Français' },
];
export default defineNuxtConfig({
runtimeConfig: {
public: {
@ -33,6 +43,7 @@ export default defineNuxtConfig({
},
},
content: {
//sources: Object.fromEntries(locales.map((e) => [e.code, { driver: 'fs', prefix: `/${e.code}`, base: resolve(__dirname, `content/${e.iso}`), }])),
navigation: {
fields: [
'date',
@ -42,14 +53,7 @@ export default defineNuxtConfig({
i18n: {
baseUrl,
vueI18n: './i18n.config.ts',
locales: [
{ code: 'ja', iso: 'ja-JP', name: '日本語' },
{ code: 'en', iso: 'en-US', name: 'English' },
{ code: 'ko', iso: 'ko-KR', name: '한국어' },
{ code: 'it', iso: 'it-IT', name: 'Italiano' },
{ code: 'pl', iso: 'pl-PL', name: 'Polski' },
{ code: 'fr', iso: 'fr-FR', name: 'Français' }
],
locales,
defaultLocale: 'ja',
strategy: 'prefix',
},

View File

@ -5,7 +5,7 @@
<h1 class="text-center font-bold text-2xl lg:text-3xl mb-4">{{ data.title }}</h1>
<p class="text-center">{{ $d(new Date(data.date)) }}</p>
</div>
<div class="bg-white dark:bg-slate-950 mb-12 lg:mt-12 pt-6">
<div class="bg-white dark:bg-slate-950 pb-12 lg:mt-12 pt-6">
<div class="mx-auto container max-w-screen-md markdown-body">
<ContentRenderer :value="data" />
</div>
@ -20,7 +20,7 @@ defineI18nRoute({
});
const route = useRoute();
const { data } = await useAsyncData(`blog-${route.params.slug}`, () => queryContent(`blog/${route.params.slug}`).findOne())
const { data } = await useAsyncData(`blog-${route.params.slug}`, () => queryContent(`/blog/${route.params.slug}`).findOne())
</script>
<style scoped>

View File

@ -15,7 +15,7 @@
</div>
</template>
</GHero>
<div class="mb-12 lg:mt-12 pt-6 bg-white dark:bg-slate-950">
<div class="pb-12 lg:mt-12 pt-6 bg-white dark:bg-slate-950">
<div class="container mx-auto max-w-screen-lg px-6 space-y-4 lg:space-y-2">
<ContentNavigation v-slot="{ navigation }" :query="blogQuery">
<GNuxtLink

View File

@ -1,13 +1,54 @@
<template>
<div>
<div class="relative container mx-auto max-w-screen-xl p-6 lg:py-0 grid docs-root pb-12">
<div class="hidden lg:block">
<div class="sticky top-16 h-[calc(100vh-4rem)] overflow-y-scroll border-r border-slate-200 dark:border-slate-700 py-6 pr-6">
</div>
</div>
<div class="lg:p-6 w-full overflow-x-hidden">
<ContentRenderer :value="data" class="markdown-body w-full">
</ContentRenderer>
</div>
<div class="hidden lg:block text-sm">
<div class="sticky top-16 h-[calc(100vh-4rem)] overflow-y-auto py-6 pl-6">
<h3 class="font-bold mb-6">{{ $t('_docs._toc.title') }}</h3>
<DocsTocLinks :links="data?.body.toc.links" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { LocaleObject } from '@nuxtjs/i18n/dist/runtime/composables';
const { locale, locales } = useI18n();
definePageMeta({
layout: 'docs',
});
defineI18nRoute({
locales: (locales.value as LocaleObject[]).map((e) => e.code),
});
const route = useRoute();
const slugs = (route.params.slug as string[]).filter((v) => v !== '');
const currentLocaleISO = () => {
if (!locales.value) {
return null;
}
return (locales.value as LocaleObject[]).find((v) => v.code === locale.value)?.iso || null;
}
const { data } = await useAsyncData(`blog-${locale.value}-${slugs.join('-')}`, () => queryContent(`/${locale.value}/docs/${slugs.join('/')}`).findOne());
route.meta.title = data.value?.title;
</script>
<style scoped>
@screen lg {
.docs-root {
grid-template-columns: 14rem 1fr 14rem;
}
}
</style>

View File

@ -15,7 +15,7 @@
</div>
</template>
</GHero>
<div class="mb-12 lg:mt-12 pt-6 bg-white dark:bg-slate-950">
<div class="pb-12 lg:mt-12 pt-6 bg-white dark:bg-slate-950">
<div class="container mx-auto max-w-screen-xl px-6 space-y-6 lg:space-y-8">
<GLocalNav :items="[
{
@ -31,7 +31,7 @@
anchor: '#forDevelopers',
}
]" />
<GNuxtLink :to="useLocalePath('/docs/about')" class="rounded-xl border border-slate-200 dark:border-accent-900 transition-colors hover:bg-slate-100 dark:hover:bg-slate-800 p-4 sm:pt-0 sm:pb-0 sm:pl-6 sm:pr-0 overflow-hidden flex">
<GNuxtLink :to="localePath(`/docs/about-misskey`)" class="rounded-xl border border-slate-200 dark:border-accent-900 transition-colors hover:bg-slate-100 dark:hover:bg-slate-800 p-4 sm:pt-0 sm:pb-0 sm:pl-6 sm:pr-0 overflow-hidden flex">
<div class="flex flex-col justify-center">
<h2 class="text-xl sm:text-2xl font-bold mb-2">{{ $t('_docs._aboutMisskey.title') }}<ArrowRightIco class="ml-2" /></h2>
<p class="text-slate-500 dark:text-slate-300">{{ $t('_docs._aboutMisskey.description') }}</p>
@ -42,9 +42,20 @@
<img class="relative h-full" src="/img/docs/mi_ai_docs.png" />
</div>
</GNuxtLink>
<GNuxtLink :to="localePath(`/docs/releases`)" class="rounded-xl border border-slate-200 dark:border-accent-900 transition-colors hover:bg-slate-100 dark:hover:bg-slate-800 p-4 sm:pt-0 sm:pb-0 sm:pl-6 sm:pr-0 overflow-hidden flex">
<div class="flex flex-col justify-center">
<h2 class="text-xl sm:text-2xl font-bold mb-2">{{ $t('_docs._changelog.title') }}<ArrowRightIco class="ml-2" /></h2>
<p class="text-slate-500 dark:text-slate-300">{{ $t('_docs._changelog.description') }}</p>
</div>
<div class="hidden sm:block ml-auto flex-shrink-0 relative py-4 pr-4 h-48 w-auto">
<GDots class="absolute top-0 right-0 h-20 w-20 text-accent-600" />
<GDots class="absolute bottom-0 -left-2 h-14 w-20 text-accent-600" />
<img class="relative h-full" src="/img/emojis/rocket_3d.png" />
</div>
</GNuxtLink>
<DocsReadersNav section-id="forUsers" id="forUsers" />
<DocsReadersNav section-id="forAdmin" id="forAdmin" />
<DocsReadersNav section-id="forDevelopers" id="forDevelopers" />
<!--<DocsReadersNav section-id="forAdmin" id="forAdmin" />
<DocsReadersNav section-id="forDevelopers" id="forDevelopers" />-->
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
<template>
<div class="relative min-h-full pb-24">
<div class="relative min-h-full">
<IndexHeroBg />
<IndexHeroParticles />
<IndexNav />
@ -7,7 +7,7 @@
<div class="relative container mx-auto p-6 md:p-8 max-w-screen-sm lg:max-w-screen-xl">
<IndexHeroLeft />
</div>
<main class="relative container mx-auto max-w-screen-xl px-6 mt-32 space-y-16">
<main class="relative container mx-auto max-w-screen-xl px-6 mt-32 mb-24 space-y-16">
<IndexKeyFeatures id="learnMore" />
<IndexDecenterized />
<GDots class="w-[95%] mx-auto text-accent-600" :space="30" />
@ -18,6 +18,7 @@
<IndexDonation />
<IndexSponsors />
</main>
<GFooter class="relative bg-transparent dark:bg-transparent" />
</div>
</template>

View File

@ -29,7 +29,7 @@
</div>
</template>
</GHero>
<div class="mb-12 lg:mt-12 pt-6 bg-white dark:bg-slate-950">
<div class="pb-12 lg:mt-12 pt-6 bg-white dark:bg-slate-950">
<div class="container mx-auto max-w-screen-xl px-6 grid server-list gap-8">
<aside class="hidden lg:block">
<div class="sticky top-24 py-2 space-y-4">
@ -90,9 +90,9 @@
<div>
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2">
<ServersItem v-if="filteredInstances.length > 0" v-for="item in filteredInstances.slice(0, f_limit)" :instance="item" />
<div v-else class="rounded-lg p-6 min-h-[40vh] flex items-center sm:col-span-2 md:col-span-2 lg:col-span-2 bg-slate-100">
<div v-else class="rounded-lg p-6 min-h-[40vh] flex items-center sm:col-span-2 md:col-span-2 lg:col-span-2 bg-slate-100 dark:bg-slate-800">
<div class="mx-auto text-center">
<img src="https://xn--931a.moe/assets/info.jpg" class="rounded mx-auto mb-4" />
<img src="https://xn--931a.moe/assets/info.jpg" class="rounded-lg mx-auto mb-4" />
<p class="max-w-xs">{{ $t('_servers._list.notFound') }}</p>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -12,6 +12,12 @@ export default <Config> {
"./app.vue",
"./error.vue",
],
safelist: [
'scroll-pt-16',
'scroll-pt-20',
'lg:scroll-pt-4',
'lg:scroll-pt-24',
],
theme: {
extend: {
colors: {