2023-07-10 19:54:13 +02:00
|
|
|
<template>
|
2024-04-06 10:06:58 +02:00
|
|
|
<div
|
|
|
|
class="grid"
|
|
|
|
:class="{
|
|
|
|
[$style.docsLayoutWithAsideToc]: shouldShowToc,
|
|
|
|
}"
|
|
|
|
>
|
2023-09-29 13:30:13 +02:00
|
|
|
<div class="lg:hidden sticky top-16 -mx-6 -mt-6 overflow-y-auto bg-slate-50 dark:bg-slate-900 z-[9890] border-b dark:border-slate-700 text-sm flex items-start">
|
2024-04-06 10:06:58 +02:00
|
|
|
<details v-if="shouldShowToc && data?.body && (data.body.toc?.links ?? []).length > 0" class="peer order-2 flex-grow flex-shrink-0" :open="openState">
|
2023-09-29 13:30:13 +02:00
|
|
|
<summary class="py-4 cursor-pointer">
|
2023-09-23 20:16:27 +02:00
|
|
|
{{ $t('_docs._toc.title') }}
|
|
|
|
</summary>
|
2023-09-26 09:09:19 +02:00
|
|
|
<div class="pb-4 px-6 max-h-[65vh] overflow-y-auto">
|
2023-11-30 13:57:24 +01:00
|
|
|
<DocsTocLinks :links="data?.body.toc?.links" :max-depth="data?.maxTocDepth ?? undefined" @child-click="openState = false" />
|
2023-09-23 20:16:27 +02:00
|
|
|
</div>
|
|
|
|
</details>
|
2023-09-29 13:30:13 +02:00
|
|
|
<button @click="isAsideNavOpen = !isAsideNavOpen" class="p-4 order-1 dark:border-slate-800 border-r peer-open:border-b mr-2">
|
|
|
|
<AsideNavIco class="block w-5 h-5" />
|
|
|
|
</button>
|
2023-09-23 20:16:27 +02:00
|
|
|
</div>
|
2024-04-06 10:06:58 +02:00
|
|
|
<div class="pt-6 p-0 sm:p-12 lg:p-6 w-full">
|
|
|
|
<Tip v-if="locale !== 'ja'" class="mb-6" :label="$t('_i18n._missing.title')">
|
|
|
|
<I18nT scope="global" keypath="_i18n._missing.description" tag="p">
|
|
|
|
<template #link>
|
|
|
|
<GNuxtLink class="font-bold hover:underline underline-offset-2" to="https://crowdin.com/project/misskey-hub" target="_blank">{{ $t('_i18n._missing.linkLabel') }}</GNuxtLink>
|
|
|
|
</template>
|
|
|
|
</I18nT>
|
|
|
|
</Tip>
|
|
|
|
<div v-if="data?._TYPE_ === 'STEPPED_GUIDE'" class="grid" :class="$style.steppedGuideRoot">
|
|
|
|
<div class="markdown-body w-full lg:col-span-2 mb-6">
|
|
|
|
<h1>{{ data.title }}</h1>
|
|
|
|
<MDC :value="data?.body" />
|
|
|
|
<div>
|
|
|
|
<label for="guideSelector" class="block">{{ $t('_docs._steppedGuide.selectCourse') }}</label>
|
|
|
|
<select id="guideSelector" class="form-select" :disabled="data.guides.length <= 1" v-model="guideIndex">
|
|
|
|
<option v-for="guide, i in data.guides" :value="i">{{ guide.title }}</option>
|
|
|
|
</select>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<Tip v-if="data.guides[guideIndex]?.description" class="mb-6 lg:col-span-2 markdown-body">
|
|
|
|
<MDC :value="data.guides[guideIndex].description" />
|
2023-12-02 06:07:37 +01:00
|
|
|
</Tip>
|
2024-04-06 10:06:58 +02:00
|
|
|
<div>
|
|
|
|
<ol class="relative before:absolute before:left-[13px] before:top-3.5 before:w-0.5 before:h-[calc(100%-.875rem)] before:rounded-full before:bg-gray-300 space-y-8">
|
|
|
|
<li
|
|
|
|
v-for="(step, i) in data.guides[guideIndex].steps"
|
|
|
|
:key="i"
|
|
|
|
:id="`steppedGuideSection_${guideIndex}_${i}`"
|
|
|
|
class="ml-7 relative flex items-center"
|
|
|
|
:class="{
|
|
|
|
'lg:min-h-[calc(100vh-4rem)] steppedGuideSection': (data.guides[guideIndex]._LAYOUT_TYPE_ === 'IMAGE_PORTRAIT_FIXED'),
|
|
|
|
}"
|
|
|
|
>
|
|
|
|
<div>
|
|
|
|
<div class="flex items-center space-x-4 mb-4">
|
|
|
|
<div class="w-7 h-7 rounded-full flex-shrink-0 -ml-7 font-bold leading-7 text-center text-white bg-accent-600 ring-4 ring-white">{{ i + 1 }}</div>
|
|
|
|
<h3 class="font-bold text-lg">{{ step.title }}</h3>
|
|
|
|
</div>
|
|
|
|
<div class="ml-4">
|
|
|
|
<img
|
|
|
|
v-if="step?.image"
|
|
|
|
:src="`/img/docs/${slugs.join('/')}/${step.image}`"
|
|
|
|
class="w-auto h-full mx-auto max-h-96 mb-4"
|
|
|
|
:class="{
|
|
|
|
'lg:hidden': (data.guides[guideIndex]._LAYOUT_TYPE_ === 'IMAGE_PORTRAIT_FIXED'),
|
|
|
|
}"
|
|
|
|
/>
|
|
|
|
<MDC v-if="step.description" :value="step.description" class="markdown-body" />
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
</ol>
|
|
|
|
</div>
|
|
|
|
<div class="hidden lg:block">
|
|
|
|
<div class="sticky top-16 h-[calc(100vh-4rem)] p-6">
|
|
|
|
<div v-if="data.guides[guideIndex]._LAYOUT_TYPE_ === 'IMAGE_PORTRAIT_FIXED'" class="relative h-full">
|
|
|
|
<Transition
|
|
|
|
:enterActiveClass="$style.steppedGuideImage_enterActive"
|
|
|
|
:leaveActiveClass="$style.steppedGuideImage_leaveActive"
|
|
|
|
:enterFromClass="$style.steppedGuideImage_enterFrom"
|
|
|
|
:leaveToClass="$style.steppedGuideImage_leaveTo"
|
|
|
|
>
|
|
|
|
<img v-if="currentStep?.image" :src="`/img/docs/${slugs.join('/')}/${currentStep.image}`" :key="`steppedGuideSection_${currentStep.image}`" class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 w-full h-auto" />
|
|
|
|
</Transition>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<template v-else-if="data?.body">
|
2023-07-17 18:43:55 +02:00
|
|
|
<ContentRenderer v-if="data.body.children.length > 0" :value="data" class="markdown-body w-full mb-6">
|
|
|
|
</ContentRenderer>
|
2023-11-12 05:38:57 +01:00
|
|
|
<div class="mt-8 mb-4 flex flex-wrap justify-end gap-3">
|
2023-12-18 09:43:48 +01:00
|
|
|
<div><GNuxtLink class="hover:underline underline-offset-4" target="_blank" :to="`${runtimeConfig.public.repositoryUrl}/tree/master/content/${data._file}`">{{ $t('_docs._contribute.editThis') }}<ExtIco class="ml-1" /></GNuxtLink></div>
|
2023-11-12 05:38:57 +01:00
|
|
|
<div><GNuxtLink class="hover:underline underline-offset-4" target="_blank" to="https://crowdin.com/project/misskey-hub">{{ $t('_docs._contribute.translateThis') }}<ExtIco class="ml-1" /></GNuxtLink></div>
|
|
|
|
</div>
|
2023-07-17 18:43:55 +02:00
|
|
|
<DocsPrevNext :ignore-dir-based-nav="data?.ignoreDirBasedNav ?? false" />
|
|
|
|
</template>
|
|
|
|
<template v-else>
|
|
|
|
<div class="markdown-body">
|
|
|
|
<h1>{{ data?.title ?? data?._dir?.title }}</h1>
|
|
|
|
<MkIndex :is-dir="data?._file?.endsWith('index.md') || (!data?._file)" />
|
|
|
|
</div>
|
|
|
|
</template>
|
2023-09-23 20:16:27 +02:00
|
|
|
</div>
|
2024-04-06 10:06:58 +02:00
|
|
|
<div v-if="shouldShowToc" class="hidden lg:block text-sm">
|
2023-07-11 19:19:32 +02:00
|
|
|
<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>
|
2023-11-11 13:59:03 +01:00
|
|
|
<DocsTocLinks v-if="data?.body" :links="data?.body.toc?.links" :max-depth="data?.maxTocDepth ?? undefined" class="break-words" />
|
2023-07-11 19:19:32 +02:00
|
|
|
</div>
|
2023-09-23 20:16:27 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
2023-07-10 19:54:13 +02:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2023-09-29 13:30:13 +02:00
|
|
|
import AsideNavIco from 'bi/text-indent-left.svg';
|
2023-11-12 05:38:57 +01:00
|
|
|
import ExtIco from 'bi/box-arrow-up-right.svg';
|
2023-11-30 13:57:24 +01:00
|
|
|
import type { MiDocsParsedContent } from '~/types/content';
|
2023-09-29 13:30:13 +02:00
|
|
|
|
|
|
|
const isAsideNavOpen = useState<boolean>('miHub_docs_asideNav_openState', () => false);
|
2023-07-11 19:19:32 +02:00
|
|
|
|
2023-12-23 17:00:03 +01:00
|
|
|
const { locale } = useI18n();
|
2023-07-15 10:48:12 +02:00
|
|
|
const openState = ref<boolean>(false);
|
2023-12-18 09:43:48 +01:00
|
|
|
const runtimeConfig = useRuntimeConfig();
|
2023-07-11 19:19:32 +02:00
|
|
|
|
|
|
|
definePageMeta({
|
2023-09-23 20:16:27 +02:00
|
|
|
layout: 'docs',
|
2023-07-11 19:19:32 +02:00
|
|
|
});
|
|
|
|
|
2024-02-09 11:39:01 +01:00
|
|
|
useHead(() => locale.value === 'ja-ks' ? ({
|
|
|
|
meta: [
|
|
|
|
{ name: 'robots', content: 'noindex' },
|
|
|
|
],
|
|
|
|
}) : ({}));
|
|
|
|
|
2023-07-11 19:19:32 +02:00
|
|
|
const route = useRoute();
|
|
|
|
const slugs = (route.params.slug as string[]).filter((v) => v !== '');
|
|
|
|
|
2024-02-09 11:39:01 +01:00
|
|
|
const { data } = await useGAsyncData(`docs-${locale.value}-${slugs.join('-')}`, () => queryContent<MiDocsParsedContent>(`/${locale.value === 'ja-ks' ? 'ja' : locale.value}/docs/${slugs.join('/')}`).findOne());
|
2023-07-11 19:19:32 +02:00
|
|
|
|
2023-07-13 18:10:25 +02:00
|
|
|
if (!data.value) {
|
2023-12-13 15:34:26 +01:00
|
|
|
throw createError({ statusCode: 404, statusMessage: 'page not found', fatal: true });
|
2023-09-23 11:17:13 +02:00
|
|
|
}
|
2023-07-13 18:10:25 +02:00
|
|
|
|
2024-04-06 10:06:58 +02:00
|
|
|
const shouldShowToc = computed(() => data.value?._TYPE_ !== 'STEPPED_GUIDE');
|
|
|
|
const guideIndex = ref<number>(0);
|
|
|
|
const { mainHeading, updateHeadings } = useScrollspy({
|
|
|
|
rootMargin: '-64px 0px 0px 0px',
|
|
|
|
threshold: 0.5,
|
|
|
|
});
|
|
|
|
const currentStep = computed(() => {
|
|
|
|
if (!mainHeading.value || data.value?._TYPE_ !== 'STEPPED_GUIDE') return null;
|
|
|
|
|
|
|
|
const [currentGuideIndex, currentStepIndex] = mainHeading.value.split('_').slice(1).map((v) => parseInt(v, 10));
|
|
|
|
return data.value.guides[currentGuideIndex].steps[currentStepIndex];
|
|
|
|
});
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
if (data.value?._TYPE_ === 'STEPPED_GUIDE') {
|
|
|
|
// User Agentを元に自動選択
|
|
|
|
if (data.value.guides.some((g) => g._AUTOSELECT_TYPE_)) {
|
|
|
|
const ua = navigator.userAgent.toLowerCase();
|
|
|
|
if (ua.includes('iphone') || ua.includes('ipad')) {
|
|
|
|
guideIndex.value = data.value.guides.findIndex((g) => g._AUTOSELECT_TYPE_ === 'OS_IOS');
|
|
|
|
} else if (ua.includes('android')) {
|
|
|
|
guideIndex.value = data.value.guides.findIndex((g) => g._AUTOSELECT_TYPE_ === 'OS_ANDROID');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
watch(guideIndex, () => {
|
|
|
|
updateHeadings([
|
|
|
|
...document.querySelectorAll('.steppedGuideSection'),
|
|
|
|
]);
|
|
|
|
}, { immediate: true });
|
|
|
|
})
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-12-30 05:31:17 +01:00
|
|
|
if (data.value._file && /index\.[a-z]+$/.test(data.value._file)) {
|
2023-12-30 05:29:37 +01:00
|
|
|
route.meta.__isDocsIndexPage = true;
|
|
|
|
}
|
|
|
|
|
2023-07-11 19:19:32 +02:00
|
|
|
route.meta.title = data.value?.title;
|
2023-12-02 05:49:50 +01:00
|
|
|
if (data.value.description) {
|
|
|
|
route.meta.description = data.value.description;
|
|
|
|
}
|
2023-07-10 19:54:13 +02:00
|
|
|
</script>
|
2023-09-23 20:16:27 +02:00
|
|
|
|
2024-04-06 10:06:58 +02:00
|
|
|
<style module>
|
|
|
|
.docsLayoutWithAsideToc,
|
|
|
|
.steppedGuideRoot {
|
2023-09-24 14:03:39 +02:00
|
|
|
grid-template-columns: 1fr;
|
|
|
|
}
|
|
|
|
|
2024-04-06 10:06:58 +02:00
|
|
|
.steppedGuideImage_enterActive,
|
|
|
|
.steppedGuideImage_leaveActive {
|
|
|
|
transition: opacity 300ms;
|
|
|
|
}
|
|
|
|
|
|
|
|
.steppedGuideImage_enterFrom,
|
|
|
|
.steppedGuideImage_leaveTo {
|
|
|
|
opacity: 0;
|
|
|
|
}
|
|
|
|
|
2023-09-23 20:16:27 +02:00
|
|
|
@screen lg {
|
2024-04-06 10:06:58 +02:00
|
|
|
.docsLayoutWithAsideToc {
|
2023-09-23 20:16:27 +02:00
|
|
|
grid-template-columns: 1fr 14rem;
|
|
|
|
}
|
2024-04-06 10:06:58 +02:00
|
|
|
|
|
|
|
.steppedGuideRoot {
|
|
|
|
grid-template-columns: 1fr 20rem;
|
|
|
|
}
|
2023-09-23 20:16:27 +02:00
|
|
|
}
|
2023-11-13 13:09:34 +01:00
|
|
|
</style>
|