(add) blog

This commit is contained in:
kakkokari-gtyih 2023-07-11 02:54:13 +09:00
parent a8dca2c347
commit 7ea64f5596
27 changed files with 424 additions and 30 deletions

24
app.vue
View File

@ -4,6 +4,7 @@ import type { Graph, Thing } from 'schema-dts';
const { t, locale } = useI18n();
const route = useRoute();
const colorMode = useColorMode();
const baseUrl = useRuntimeConfig().public.baseUrl as string;
const getDescription = (): string => {
if (route.meta.description != null && route.meta.description != "") {
@ -19,9 +20,9 @@ const getLdJson = (additionalGraphes: Thing[] = []): string => {
"@graph": [
{
"@type": "Organization",
"@id": "https://misskey-hub.net/#Organization",
"@id": `${baseUrl}/#Organization`,
"name": "Misskey",
"url": `https://misskey-hub.net/`,
"url": `${baseUrl}/`,
"sameAs": [
"https://join.misskey.page/",
"https://ja.wikipedia.org/wiki/Misskey",
@ -29,18 +30,18 @@ const getLdJson = (additionalGraphes: Thing[] = []): string => {
"logo": {
"@type": "ImageObject",
// TODO
"url": "https://misskey-hub.net/img/logo.png"
"url": `${baseUrl}/img/logo.png`
}
},
{
"@type": "WebSite",
"@id": "https://misskey-hub.net/#WebPage",
"@id": `${baseUrl}/#WebPage`,
"name": t('_seo.siteName'),
"inLanguage": locale.value,
"url": `https://misskey-hub.net${route.path}`,
"url": `${baseUrl}${route.path}`,
"publisher": {
"@type": "Organization",
"@id": "https://misskey-hub.net/#Organization"
"@id": `${baseUrl}/#Organization`
},
"headline": getTitle(),
"description": getDescription()
@ -51,6 +52,10 @@ const getLdJson = (additionalGraphes: Thing[] = []): string => {
return JSON.stringify(ldJson);
};
const head = useLocaleHead({
addSeoAttributes: true
});
useHead((): Record<string, any> => ({
htmlAttrs: {
lang: locale.value,
@ -73,11 +78,12 @@ useHead((): Record<string, any> => ({
{
property: "og:image",
// TODO
content: () => route.meta.thumbnail ? route.meta.thumbnail : "https://misskey-hub.net/img/logo.jpg",
}
content: () => route.meta.thumbnail ? route.meta.thumbnail : `${baseUrl}/img/logo.jpg`,
},
...(head.value.meta?.map((e) => ({ property: e.property, content: e.content, })) || []),
],
link: [
{ rel: "canonical", href: "https://misskey-hub.net" + route.path },
...(head.value.link?.map((e) => ({ rel: e.rel, href: (e.href.endsWith('/') ? e.href : e.href + '/'), hreflang: e.hreflang, })) || []),
],
script: [
{ type: "application/ld+json", children: getLdJson(route.meta.graph) }

View File

@ -26,3 +26,121 @@ body {
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Github Markdown Css Override */
html.dark .markdown-body {
color-scheme: dark;
--color-prettylights-syntax-comment: #8b949e;
--color-prettylights-syntax-constant: #79c0ff;
--color-prettylights-syntax-entity: #d2a8ff;
--color-prettylights-syntax-storage-modifier-import: #c9d1d9;
--color-prettylights-syntax-entity-tag: #7ee787;
--color-prettylights-syntax-keyword: #ff7b72;
--color-prettylights-syntax-string: #a5d6ff;
--color-prettylights-syntax-variable: #ffa657;
--color-prettylights-syntax-brackethighlighter-unmatched: #f85149;
--color-prettylights-syntax-invalid-illegal-text: #f0f6fc;
--color-prettylights-syntax-invalid-illegal-bg: #8e1519;
--color-prettylights-syntax-carriage-return-text: #f0f6fc;
--color-prettylights-syntax-carriage-return-bg: #b62324;
--color-prettylights-syntax-string-regexp: #7ee787;
--color-prettylights-syntax-markup-list: #f2cc60;
--color-prettylights-syntax-markup-heading: #1f6feb;
--color-prettylights-syntax-markup-italic: #c9d1d9;
--color-prettylights-syntax-markup-bold: #c9d1d9;
--color-prettylights-syntax-markup-deleted-text: #ffdcd7;
--color-prettylights-syntax-markup-deleted-bg: #67060c;
--color-prettylights-syntax-markup-inserted-text: #aff5b4;
--color-prettylights-syntax-markup-inserted-bg: #033a16;
--color-prettylights-syntax-markup-changed-text: #ffdfb6;
--color-prettylights-syntax-markup-changed-bg: #5a1e02;
--color-prettylights-syntax-markup-ignored-text: #c9d1d9;
--color-prettylights-syntax-markup-ignored-bg: #1158c7;
--color-prettylights-syntax-meta-diff-range: #d2a8ff;
--color-prettylights-syntax-brackethighlighter-angle: #8b949e;
--color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;
--color-prettylights-syntax-constant-other-reference-link: #a5d6ff;
--color-fg-default: #c9d1d9;
--color-fg-muted: #8b949e;
--color-fg-subtle: #6e7681;
--color-canvas-default: #0d1117;
--color-canvas-subtle: #161b22;
--color-border-default: #30363d;
--color-border-muted: #21262d;
--color-neutral-muted: rgba(110, 118, 129, 0.4);
--color-accent-fg: #58a6ff;
--color-accent-emphasis: #1f6feb;
--color-attention-subtle: rgba(187, 128, 9, 0.15);
--color-danger-fg: #f85149;
}
html.light .markdown-body {
color-scheme: light;
--color-prettylights-syntax-comment: #6e7781;
--color-prettylights-syntax-constant: #0550ae;
--color-prettylights-syntax-entity: #8250df;
--color-prettylights-syntax-storage-modifier-import: #24292f;
--color-prettylights-syntax-entity-tag: #116329;
--color-prettylights-syntax-keyword: #cf222e;
--color-prettylights-syntax-string: #0a3069;
--color-prettylights-syntax-variable: #953800;
--color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
--color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
--color-prettylights-syntax-invalid-illegal-bg: #82071e;
--color-prettylights-syntax-carriage-return-text: #f6f8fa;
--color-prettylights-syntax-carriage-return-bg: #cf222e;
--color-prettylights-syntax-string-regexp: #116329;
--color-prettylights-syntax-markup-list: #3b2300;
--color-prettylights-syntax-markup-heading: #0550ae;
--color-prettylights-syntax-markup-italic: #24292f;
--color-prettylights-syntax-markup-bold: #24292f;
--color-prettylights-syntax-markup-deleted-text: #82071e;
--color-prettylights-syntax-markup-deleted-bg: #ffebe9;
--color-prettylights-syntax-markup-inserted-text: #116329;
--color-prettylights-syntax-markup-inserted-bg: #dafbe1;
--color-prettylights-syntax-markup-changed-text: #953800;
--color-prettylights-syntax-markup-changed-bg: #ffd8b5;
--color-prettylights-syntax-markup-ignored-text: #eaeef2;
--color-prettylights-syntax-markup-ignored-bg: #0550ae;
--color-prettylights-syntax-meta-diff-range: #8250df;
--color-prettylights-syntax-brackethighlighter-angle: #57606a;
--color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
--color-prettylights-syntax-constant-other-reference-link: #0a3069;
--color-fg-default: #24292f;
--color-fg-muted: #57606a;
--color-fg-subtle: #6e7781;
--color-canvas-default: #ffffff;
--color-canvas-subtle: #f6f8fa;
--color-border-default: #d0d7de;
--color-border-muted: hsla(210, 18%, 87%, 1);
--color-neutral-muted: rgba(175, 184, 193, 0.2);
--color-accent-fg: #0969da;
--color-accent-emphasis: #0969da;
--color-attention-subtle: #fff8c5;
--color-danger-fg: #cf222e;
}
.markdown-body {
--color-canvas-default: transparent!important;
@apply font-content-sans;
}
.markdown-body h1 > a[href^='#'],
.markdown-body h2 > a[href^='#'],
.markdown-body h3 > a[href^='#'],
.markdown-body h4 > a[href^='#'],
.markdown-body h5 > a[href^='#'],
.markdown-body h6 > a[href^='#'] {
all: unset;
color: var(--color-fg-default);
cursor: pointer;
}
.markdown-body ul {
list-style: disc;
}
.markdown-body ol {
list-style: decimal;
}

View File

@ -0,0 +1,15 @@
<template>
<div class="root bg-red-100 dark:bg-red-900 dark:bg-opacity-25 border border-red-200 dark:border-red-500 rounded-lg px-4 py-3 mb-4">
<slot></slot>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.root > ::v-deep(*:last-child) {
margin-bottom: 0;
}
</style>

View File

@ -0,0 +1,21 @@
<script setup lang="ts">
defineProps<{
summary?: string;
}>();
</script>
<template>
<details class="group my-4">
<summary class="cursor-pointer outline-none p-2 border rounded-lg bg-white shadow-md group-open:rounded-b-none group-open:bg-slate-200 group-open:shadow-none group-open:border-b-0">
{{summary}}
</summary>
<div class="rounded-b-lg border p-3 bg-white space-y-2">
<slot></slot>
</div>
</details>
</template>
<style scoped>
summary::after {
@apply text-sm hidden md:inline;
content: "(クリックで展開)";
}
</style>

View File

@ -0,0 +1,15 @@
<template>
<div class="root bg-sky-100 dark:bg-sky-900 dark:bg-opacity-25 border border-sky-200 dark:border-sky-500 rounded-lg px-4 py-3 mb-4">
<slot></slot>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.root > ::v-deep(*:last-child) {
margin-bottom: 0;
}
</style>

View File

@ -0,0 +1,15 @@
<template>
<div class="root bg-yellow-100 dark:bg-yellow-900 dark:bg-opacity-25 border border-yellow-200 dark:border-yellow-500 rounded-lg px-4 py-3 mb-4">
<slot></slot>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
.root > ::v-deep(*:last-child) {
margin-bottom: 0;
}
</style>

View File

@ -0,0 +1,24 @@
<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>
</section>
</template>
<script setup lang="ts">
import ArrowRightIco from 'bi/arrow-right.svg';
defineProps<{
sectionId: 'forUsers' | 'forAdmin' | 'forDevelopers';
}>();
</script>
<style scoped>
</style>

59
components/g/LocalNav.vue Normal file
View File

@ -0,0 +1,59 @@
<template>
<ul class="flex flex-wrap p-4" :class="$style.localNavRoot">
<li v-for="item in items">
<button v-if="item.anchor" class="hover:text-slate-950 focus:text-slate-950 dark:hover:text-slate-200 dark:focus:text-slate-200 " @click="scrollTo(item.anchor)">
{{ item.name }}
<ArrowIco class="ml-2 stroke-1 stroke-current" />
</button>
</li>
</ul>
</template>
<script setup lang="ts">
import ArrowIco from 'bi/arrow-down.svg';
interface LocalNavItem {
name: string;
to?: string;
anchor?: string;
}
const props = defineProps<{
items: LocalNavItem[]
}>();
function scrollTo(qs: string): void {
if (process.client) {
document.querySelector<HTMLElement>(`${qs} a, ${qs} button, ${qs} input, ${qs} textarea`)?.focus({
preventScroll: true,
});
document.querySelector(qs)?.scrollIntoView({
behavior: "smooth"
});
}
}
</script>
<style module>
.localNavRoot li a,
.localNavRoot li button {
@apply font-bold tracking-wide relative px-2 py-4 text-slate-400 transition-colors;
}
.localNavRoot li:not(:last-child) {
@apply mr-4;
}
@media (max-width: 768px) {
.localNavRoot li:not(:last-child) {
@apply mr-0;
}
.localNavRoot li a,
.localNavRoot li button {
@apply w-full text-sm text-end
}
.localNavRoot li {
width: calc((100% - 24px) / 2);
}
.localNavRoot li:nth-child(2n) {
margin-left: 24px;
}
}
</style>

View File

@ -63,7 +63,7 @@ Misskey Hubは、Misskey Hub内のMisskey Hub説明ページにもあるよう
動的サイトを運営する際に発生するそのような諸々の心配から解放されるというのは非常に大きなメリットです。つまり、金銭面においても精神面においても、**ゼロコスト**でMisskey Hubを運用できます。
:::details さらに技術的な話(危険)
:::g-details{summary="さらに技術的な話(危険)"}
ここまで言っておいてなんですが、先ほど言った
> もちろんMisskeyは動的サイトということになります。
@ -239,7 +239,7 @@ SSGはMisskey Hubのようなドキュメントサイトだけでなく、ブロ
静的サイトはいいぞ。vuepressはいいぞ。Misskey Hubに幸あれ🙏
:::details Webで記事を公開する際に必ず記述しなければならないと法令で定められている言葉
:::g-details{summary="Webで記事を公開する際に必ず記述しなければならないと法令で定められている言葉"}
> いかがでしたか?
:::

View File

@ -8,7 +8,7 @@ You can select an instance of your choice from the [instance list](../instances.
:::tip
No matter which instance you choose, you will be able to interact with users on other instances.
:::details
:::g-details
Misskey has the ability to federate.
Misskey has built-in federation support, which allows users in different instances to follow, react, and renote in the same way as users on the same instance.

View File

@ -114,7 +114,7 @@ Just `NODE_ENV=production pnpm run start`. GLHF!
2. Edit it, and paste this and save:
:::details
:::g-details
```
[Unit]
Description=Misskey daemon
@ -150,7 +150,7 @@ You can check if the service is running with `systemctl status misskey`.
1. Copy the following text to `/etc/init.d/misskey`:
:::details
:::g-details
```sh
#!/sbin/openrc-run

View File

@ -113,7 +113,7 @@ Just `NODE_ENV=production pnpm run start`. GLHF!
2. Edit it, and paste this and save:
:::details
:::g-details
```
[Unit]
Description=Misskey daemon
@ -149,7 +149,7 @@ You can check if the service is running with `systemctl status misskey`.
1. Copy the following text to `/etc/init.d/misskey`:
:::details
:::g-details
```sh
#!/sbin/openrc-run

View File

@ -7,7 +7,7 @@ Per entrare subito nel Fediverso, crea il tuo profilo su una delle [Istanze Miss
:::tip
Qualsiasi Istanza tu scelga, potrai interagire con i profili delle persone registrate su altre istanze.
:::details
:::g-details
Misskey ha la capacità di federarsi e scambiare informazioni tramite protocollo ActivityPub.
In questo modo puoi comunicare con istanze di tipo Mastodon, Pixelfed, PeerTube, Soapbox o Pleroma.

View File

@ -8,7 +8,7 @@ Misskeyを利用するには、どこかのサーバーにアカウントを作
:::tip
どのサーバーを選んでも、他のサーバーのユーザーとやり取りすることができます。
:::details
:::g-details
Misskeyには連合機能が備わっています。
連合機能によって、別のサーバーのユーザーであっても、同じサーバーのユーザーと同じようにフォローやリアクション、Renoteなどができるようになっています。

View File

@ -76,7 +76,7 @@ NODE_ENV=production pnpm run start
GLHF✨
::::details systemdを用いた管理
::::g-details{summary="systemdを用いた管理"}
systemdサービスのファイルを作成

View File

@ -8,7 +8,7 @@ Misskey는 프로그램, 그리고 이를 관리하는 프로젝트의 이름입
:::tip
어떤 인스턴스를 선택하더라도, 다른 인스턴스의 유저와 상호작용할 수 있습니다.
:::details
:::g-details
Misskey는 '연합' 기능을 갖추고 있습니다.
연합 기능을 통해, 다른 인스턴스에 있는 유저라도 같은 인스턴스의 유저와 동일하게 팔로우 및 리액션, Renote 등을 할 수 있습니다.

View File

@ -124,7 +124,7 @@ Wystarczy `NODE_ENV=production pnpm run start` i GLHF!
2. Edytuj plik i wklej następujący kod:
:::details
:::g-details
```
[Unit]
@ -166,7 +166,7 @@ Możesz sprawdzić działanie usługi wpisując
1. Skopiuj poniższy tekst do `/etc/init.d/misskey`:
:::details
:::g-details
```sh
#!/sbin/openrc-run

View File

@ -7,5 +7,25 @@ export default defineI18nConfig(() => ({
messages: {
ja,
en,
},
datetimeFormats: {
'en-US': {
short: {
year: 'numeric', month: 'short', day: 'numeric'
},
long: {
year: 'numeric', month: 'short', day: 'numeric',
weekday: 'short', hour: 'numeric', minute: 'numeric'
}
},
'ja-JP': {
short: {
year: 'numeric', month: 'short', day: 'numeric'
},
long: {
year: 'numeric', month: 'short', day: 'numeric',
weekday: 'short', hour: 'numeric', minute: 'numeric', hour12: true
}
}
}
}));

View File

@ -1,4 +1,4 @@
# **DO NOT edit locale files** except `ja-JP.yml`.
When you add text to the ja-JP file (of misskey-dev/misskey), it will automatically be applied to other language files.
When you add text to the ja-JP file (of misskey-dev/misskey-hub), it will automatically be applied to other language files.
Translations added in ja-JP file should contain the original Japanese strings.

View File

@ -105,6 +105,15 @@ _servers:
_docs:
title: "ドキュメント"
description: "Misskeyの上手なつかいかたから、サーバーの運営者・プログラムの開発者向けの情報まで網羅しています。"
_aboutMisskey:
title: "Misskeyについて"
description: "Misskeyをはじめて知ったかたや、これから使うかた向け基本的なしくみや機能を見ていきましょう。"
_forUsers:
title: "Misskeyユーザー向け"
_forAdmin:
title: "サーバー運営者向け"
_forDevelopers:
title: "開発者向け"
_blog:
title: "ブログ"
description: "Misskey開発本部から、Misskeyに関する最新情報やTips等をお届けします"
description: "Misskey開発本部から、Misskeyに関する最新情報やTips等をお届けします(日本語のみ)"

View File

@ -3,8 +3,17 @@ import ViteYaml from '@modyfi/vite-plugin-yaml';
import svgLoader from 'vite-svg-loader';
import genSitemap from './scripts/gen-sitemap';
// 公開時のドメイン(末尾スラッシュなし)
const baseUrl = 'https://misskey-hub.net';
export default defineNuxtConfig({
runtimeConfig: {
public: {
baseUrl,
}
},
css: [
"github-markdown-css/github-markdown.css",
"@/assets/css/tailwind.css",
"@/assets/css/bootstrap-forms.scss",
],
@ -23,7 +32,15 @@ export default defineNuxtConfig({
],
},
},
content: {
navigation: {
fields: [
'date',
]
}
},
i18n: {
baseUrl,
vueI18n: './i18n.config.ts',
locales: [
{ code: 'ja', iso: 'ja-JP', name: '日本語' },

View File

@ -1,7 +1,16 @@
<template>
<div>
</div>
<div class="min-h-[150px] flex flex-col justify-center">
<p class="text-center mb-4">{{ $t('_blog.title') }}</p>
<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="mx-auto container max-w-screen-md markdown-body">
<ContentRenderer :value="data" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
@ -9,6 +18,9 @@
defineI18nRoute({
locales: ['ja'],
});
const route = useRoute();
const { data } = await useAsyncData(`blog-${route.params.slug}`, () => queryContent(`blog/${route.params.slug}`).findOne())
</script>
<style scoped>

View File

@ -15,12 +15,27 @@
</div>
</template>
</GHero>
<div class="mb-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
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"
v-for="item in navigation[0].children"
:key="item._path"
:to="localePath(item._path, 'ja')"
>
<h3 class="text-lg font-bold mb-2">{{ item.navTitle || item.title }}</h3>
<p class="text-sm">{{ item.date ? $d(new Date(item.date)) : '' }}</p>
</GNuxtLink>
</ContentNavigation>
</div>
</div>
</div>
</template>
<script setup lang="ts">
const blogQuery = queryContent('blog').sort({ date: -1 });
const localePath = useLocalePath();
</script>
<style scoped>

13
pages/docs/[...slug].vue Normal file
View File

@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script setup lang="ts">
</script>
<style scoped>
</style>

View File

@ -15,12 +15,45 @@
</div>
</template>
</GHero>
<div class="mb-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="[
{
name: $t('_docs._forUsers.title'),
anchor: '#forUsers',
},
{
name: $t('_docs._forAdmin.title'),
anchor: '#forAdmin',
},
{
name: $t('_docs._forDevelopers.title'),
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">
<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>
</div>
<div class="hidden sm:block ml-auto flex-shrink-0 relative pt-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/docs/mi_ai_docs.png" />
</div>
</GNuxtLink>
<DocsReadersNav section-id="forUsers" id="forUsers" />
<DocsReadersNav section-id="forAdmin" id="forAdmin" />
<DocsReadersNav section-id="forDevelopers" id="forDevelopers" />
</div>
</div>
</div>
</template>
<script setup lang="ts">
import ArrowRightIco from 'bi/arrow-right.svg';
const localePath = useLocalePath();
</script>
<style scoped>

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

View File

@ -1,11 +1,13 @@
import type { Nitro } from "nitropack";
//import { useRuntimeConfig } from "nuxt/app";
import { SitemapStream, streamToPromise, SitemapItem } from 'sitemap';
import { Readable } from 'stream';
import { writeFileSync } from 'fs';
import path from 'path';
// サイトドメインを指定(最後スラッシュ不要)
const domain = "https://YOUR_DOMAIN";
//const domain = useRuntimeConfig().public.baseUrl;
const domain = "your-app.com";
export default async function genSitemap(nitro: Nitro) {
if (!nitro._prerenderedRoutes) {