mirror of
https://iceshrimp.dev/Crimekillz/jointrashposs.git
synced 2024-11-25 10:19:07 +01:00
(add) docs page
This commit is contained in:
parent
2ce3522e05
commit
7aec066b8d
22 changed files with 507 additions and 49 deletions
64
components/content/MkIndex.vue
Normal file
64
components/content/MkIndex.vue
Normal 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>
|
43
components/content/ProseA.vue
Normal file
43
components/content/ProseA.vue
Normal 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>
|
7
components/content/ProseTable.vue
Normal file
7
components/content/ProseTable.vue
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<template>
|
||||||
|
<div class="w-full overflow-x-auto whitespace-nowrap">
|
||||||
|
<table>
|
||||||
|
<slot />
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
44
components/docs/PrevNext.vue
Normal file
44
components/docs/PrevNext.vue
Normal 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>
|
|
@ -1,24 +1,33 @@
|
||||||
<template>
|
<template>
|
||||||
<section>
|
<section>
|
||||||
<h2 class="text-2xl lg:text-3xl font-bold mb-4">{{ $t(`_docs._${sectionId}.title`) }}</h2>
|
<h2 class="text-2xl lg:text-3xl font-bold mb-4">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
{{ $t(`_docs._${sectionId}.title`) }}
|
||||||
<div v-for="n of 6" class="border rounded-lg p-4">
|
</h2>
|
||||||
<h3 class="font-bold text-lg mb-2">大カテゴリタイトル<ArrowRightIco class="ml-1.5" /></h3>
|
<MkIndex :wide="true" :base-path="basePath" />
|
||||||
<p class="text-sm opacity-80">カテゴリ説明文</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import ArrowRightIco from 'bi/arrow-right.svg';
|
const props = defineProps<{
|
||||||
|
sectionId: "forUsers" | "forAdmin" | "forDevelopers";
|
||||||
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
76
components/docs/TocLinks.vue
Normal file
76
components/docs/TocLinks.vue
Normal 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>
|
|
@ -1,11 +1,74 @@
|
||||||
<template>
|
<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 & 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">© 2023 Misskey, syuilo, and other contributors
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import MiIcon from '@/assets/svg/misskey_mi_bi.svg';
|
||||||
|
|
||||||
|
const localePath = useLocalePath();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<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 }]">
|
<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 h-16 lg:h-20 grid items-center grid-cols-2 md:grid-cols-4 lg:grid-cols-6 p-4">
|
<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="">
|
<div class="">
|
||||||
<GNuxtLink :to="localePath('/')" class="flex items-center space-x-2 hover:opacity-80">
|
<GNuxtLink :to="localePath('/')" class="flex items-center space-x-2 hover:opacity-80">
|
||||||
<MiIcon class="h-8 w-8" />
|
<MiIcon class="h-8 w-8" />
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup lang="ts">
|
||||||
import MiIcon from '@/assets/svg/misskey_mi_bi.svg';
|
import MiIcon from '@/assets/svg/misskey_mi_bi.svg';
|
||||||
import I18nIcon from 'bi/translate.svg';
|
import I18nIcon from 'bi/translate.svg';
|
||||||
import SunIcon from 'bi/sun.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 DisplayIcon from 'bi/display.svg';
|
||||||
import NavData from '@/assets/data/nav';
|
import NavData from '@/assets/data/nav';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
disableShadow?: boolean;
|
||||||
|
slim?: boolean;
|
||||||
|
}>(), {
|
||||||
|
disableShadow: false,
|
||||||
|
slim: false,
|
||||||
|
});
|
||||||
|
|
||||||
const { locales, locale: currentLocale } = useI18n();
|
const { locales, locale: currentLocale } = useI18n();
|
||||||
const { path } = useRoute();
|
const { path } = useRoute();
|
||||||
const switchLocalePath = useSwitchLocalePath();
|
const switchLocalePath = useSwitchLocalePath();
|
||||||
|
@ -83,13 +91,13 @@ async function updatePos() {
|
||||||
scrollPos.value = document.body.getBoundingClientRect().y;
|
scrollPos.value = document.body.getBoundingClientRect().y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.client) {
|
if (process.client && !props.disableShadow) {
|
||||||
window.addEventListener('scroll', updatePos);
|
window.addEventListener('scroll', updatePos);
|
||||||
window.addEventListener('resize', updatePos);
|
window.addEventListener('resize', updatePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
if (process.client) {
|
if (process.client && !props.disableShadow) {
|
||||||
window.removeEventListener('scroll', updatePos);
|
window.removeEventListener('scroll', updatePos);
|
||||||
window.removeEventListener('resize', updatePos);
|
window.removeEventListener('resize', updatePos);
|
||||||
}
|
}
|
||||||
|
|
39
composables/useScrollSpy.ts
Normal file
39
composables/useScrollSpy.ts
Normal 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,14 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const isNavOpen = ref<boolean>(false);
|
const isNavOpen = ref<boolean>(false);
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
htmlAttrs: {
|
||||||
|
class: 'scroll-pt-20 lg:scroll-pt-24',
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="">
|
||||||
<GNav @toggleNav="isNavOpen = !isNavOpen" :is-open="isNavOpen" />
|
<GNav @toggleNav="isNavOpen = !isNavOpen" :is-open="isNavOpen" />
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
|
18
layouts/docs.vue
Normal file
18
layouts/docs.vue
Normal 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>
|
|
@ -1,11 +1,16 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const isNavOpen = ref<boolean>(false);
|
const isNavOpen = ref<boolean>(false);
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
htmlAttrs: {
|
||||||
|
class: 'scroll-pt-20 lg:scroll-pt-4',
|
||||||
|
},
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
<GFooter />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -105,15 +105,28 @@ _servers:
|
||||||
_docs:
|
_docs:
|
||||||
title: "ドキュメント"
|
title: "ドキュメント"
|
||||||
description: "Misskeyの上手なつかいかたから、サーバーの運営者・プログラムの開発者向けの情報まで網羅しています。"
|
description: "Misskeyの上手なつかいかたから、サーバーの運営者・プログラムの開発者向けの情報まで網羅しています。"
|
||||||
|
indexTitle: "目次ページ"
|
||||||
_aboutMisskey:
|
_aboutMisskey:
|
||||||
title: "Misskeyについて"
|
title: "Misskeyについて"
|
||||||
description: "Misskeyをはじめて知ったかたや、これから使うかた向け!基本的なしくみや機能を見ていきましょう。"
|
description: "Misskeyをはじめて知ったかたや、これから使うかた向け!基本的なしくみや機能を見ていきましょう。"
|
||||||
|
_changelog:
|
||||||
|
title: "リリースノート"
|
||||||
|
description: "Misskeyのバージョンアップ履歴をご覧いただけます。最新のバージョンで何が変わったのかを確認しましょう!"
|
||||||
_forUsers:
|
_forUsers:
|
||||||
title: "Misskeyユーザー向け"
|
title: "Misskeyユーザー向け"
|
||||||
_forAdmin:
|
_forAdmin:
|
||||||
title: "サーバー運営者向け"
|
title: "サーバー運営者向け"
|
||||||
_forDevelopers:
|
_forDevelopers:
|
||||||
title: "開発者向け"
|
title: "開発者向け"
|
||||||
|
_toc:
|
||||||
|
title: "このページの内容"
|
||||||
|
toPageTop: "ページ上部に戻る"
|
||||||
_blog:
|
_blog:
|
||||||
title: "ブログ"
|
title: "ブログ"
|
||||||
description: "Misskey開発本部から、Misskeyに関する最新情報やTips等をお届けします!(日本語のみ)"
|
description: "Misskey開発本部から、Misskeyに関する最新情報やTips等をお届けします!(日本語のみ)"
|
||||||
|
_other:
|
||||||
|
title: "お楽しみ"
|
||||||
|
_brandAssets:
|
||||||
|
title: "アセット集"
|
||||||
|
_links:
|
||||||
|
title: "リンク"
|
|
@ -2,10 +2,20 @@
|
||||||
import ViteYaml from '@modyfi/vite-plugin-yaml';
|
import ViteYaml from '@modyfi/vite-plugin-yaml';
|
||||||
import svgLoader from 'vite-svg-loader';
|
import svgLoader from 'vite-svg-loader';
|
||||||
import genSitemap from './scripts/gen-sitemap';
|
import genSitemap from './scripts/gen-sitemap';
|
||||||
|
import { resolve } from 'path';
|
||||||
|
|
||||||
// 公開時のドメイン(末尾スラッシュなし)
|
// 公開時のドメイン(末尾スラッシュなし)
|
||||||
const baseUrl = 'https://misskey-hub.net';
|
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({
|
export default defineNuxtConfig({
|
||||||
runtimeConfig: {
|
runtimeConfig: {
|
||||||
public: {
|
public: {
|
||||||
|
@ -33,6 +43,7 @@ export default defineNuxtConfig({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
|
//sources: Object.fromEntries(locales.map((e) => [e.code, { driver: 'fs', prefix: `/${e.code}`, base: resolve(__dirname, `content/${e.iso}`), }])),
|
||||||
navigation: {
|
navigation: {
|
||||||
fields: [
|
fields: [
|
||||||
'date',
|
'date',
|
||||||
|
@ -42,14 +53,7 @@ export default defineNuxtConfig({
|
||||||
i18n: {
|
i18n: {
|
||||||
baseUrl,
|
baseUrl,
|
||||||
vueI18n: './i18n.config.ts',
|
vueI18n: './i18n.config.ts',
|
||||||
locales: [
|
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' }
|
|
||||||
],
|
|
||||||
defaultLocale: 'ja',
|
defaultLocale: 'ja',
|
||||||
strategy: 'prefix',
|
strategy: 'prefix',
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<h1 class="text-center font-bold text-2xl lg:text-3xl mb-4">{{ data.title }}</h1>
|
<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>
|
<p class="text-center">{{ $d(new Date(data.date)) }}</p>
|
||||||
</div>
|
</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">
|
<div class="mx-auto container max-w-screen-md markdown-body">
|
||||||
<ContentRenderer :value="data" />
|
<ContentRenderer :value="data" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -20,7 +20,7 @@ defineI18nRoute({
|
||||||
});
|
});
|
||||||
|
|
||||||
const route = useRoute();
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</GHero>
|
</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">
|
<div class="container mx-auto max-w-screen-lg px-6 space-y-4 lg:space-y-2">
|
||||||
<ContentNavigation v-slot="{ navigation }" :query="blogQuery">
|
<ContentNavigation v-slot="{ navigation }" :query="blogQuery">
|
||||||
<GNuxtLink
|
<GNuxtLink
|
||||||
|
|
|
@ -1,13 +1,54 @@
|
||||||
<template>
|
<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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@screen lg {
|
||||||
|
.docs-root {
|
||||||
|
grid-template-columns: 14rem 1fr 14rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -15,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</GHero>
|
</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">
|
<div class="container mx-auto max-w-screen-xl px-6 space-y-6 lg:space-y-8">
|
||||||
<GLocalNav :items="[
|
<GLocalNav :items="[
|
||||||
{
|
{
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
anchor: '#forDevelopers',
|
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">
|
<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>
|
<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>
|
<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" />
|
<img class="relative h-full" src="/img/docs/mi_ai_docs.png" />
|
||||||
</div>
|
</div>
|
||||||
</GNuxtLink>
|
</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="forUsers" id="forUsers" />
|
||||||
<DocsReadersNav section-id="forAdmin" id="forAdmin" />
|
<!--<DocsReadersNav section-id="forAdmin" id="forAdmin" />
|
||||||
<DocsReadersNav section-id="forDevelopers" id="forDevelopers" />
|
<DocsReadersNav section-id="forDevelopers" id="forDevelopers" />-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="relative min-h-full pb-24">
|
<div class="relative min-h-full">
|
||||||
<IndexHeroBg />
|
<IndexHeroBg />
|
||||||
<IndexHeroParticles />
|
<IndexHeroParticles />
|
||||||
<IndexNav />
|
<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">
|
<div class="relative container mx-auto p-6 md:p-8 max-w-screen-sm lg:max-w-screen-xl">
|
||||||
<IndexHeroLeft />
|
<IndexHeroLeft />
|
||||||
</div>
|
</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" />
|
<IndexKeyFeatures id="learnMore" />
|
||||||
<IndexDecenterized />
|
<IndexDecenterized />
|
||||||
<GDots class="w-[95%] mx-auto text-accent-600" :space="30" />
|
<GDots class="w-[95%] mx-auto text-accent-600" :space="30" />
|
||||||
|
@ -18,6 +18,7 @@
|
||||||
<IndexDonation />
|
<IndexDonation />
|
||||||
<IndexSponsors />
|
<IndexSponsors />
|
||||||
</main>
|
</main>
|
||||||
|
<GFooter class="relative bg-transparent dark:bg-transparent" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</GHero>
|
</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">
|
<div class="container mx-auto max-w-screen-xl px-6 grid server-list gap-8">
|
||||||
<aside class="hidden lg:block">
|
<aside class="hidden lg:block">
|
||||||
<div class="sticky top-24 py-2 space-y-4">
|
<div class="sticky top-24 py-2 space-y-4">
|
||||||
|
@ -90,9 +90,9 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2">
|
<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" />
|
<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">
|
<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>
|
<p class="max-w-xs">{{ $t('_servers._list.notFound') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
BIN
public/img/emojis/rocket_3d.png
Normal file
BIN
public/img/emojis/rocket_3d.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
|
@ -12,6 +12,12 @@ export default <Config> {
|
||||||
"./app.vue",
|
"./app.vue",
|
||||||
"./error.vue",
|
"./error.vue",
|
||||||
],
|
],
|
||||||
|
safelist: [
|
||||||
|
'scroll-pt-16',
|
||||||
|
'scroll-pt-20',
|
||||||
|
'lg:scroll-pt-4',
|
||||||
|
'lg:scroll-pt-24',
|
||||||
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
|
|
Loading…
Reference in a new issue